Skip to content

Commit

Permalink
Merge pull request #166 from jaraco/feature/validate-more
Browse files Browse the repository at this point in the history
Additionally validate words in other public methods.
  • Loading branch information
jaraco authored Jul 30, 2022
2 parents 220b3bd + 5df0567 commit 77e7587
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 26 deletions.
12 changes: 11 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
v5.7.0
v6.0.0
======

* #157: ``compare`` methods now validate their inputs
and will raise a more meaningful exception if an
empty string or None is passed. This expectation is now
documented.

* Many public methods now perform validation on arguments.
An empty string is no longer allowed for words or text.
Callers are expected to pass non-empty text or trap
the validation errors that are raised. The exceptions
raised are ``pydantic.error_wrappers.ValidationError``,
which are currently a subclass of ``ValueError``, but since
that
`may change <https://pydantic-docs.helpmanual.io/usage/validation_decorator/#validation-exception>`_,
tests check for a generic ``Exception``.

v5.6.2
======

Expand Down
60 changes: 42 additions & 18 deletions inflect/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@
Callable,
Sequence,
cast,
Any,
)
from numbers import Number


from pydantic import Field, validate_arguments
Expand Down Expand Up @@ -2038,6 +2040,7 @@ def __init__(self, orig) -> None:


Word = Annotated[str, Field(min_length=1)]
Falsish = Any # ideally, falsish would only validate on bool(value) is False


class engine:
Expand Down Expand Up @@ -2156,14 +2159,15 @@ def checkpatplural(self, pattern: str) -> None:
"""
return

def ud_match(self, word: str, wordlist: List[str]) -> Optional[str]:
@validate_arguments
def ud_match(self, word: Word, wordlist: Sequence[Optional[Word]]) -> Optional[str]:
for i in range(len(wordlist) - 2, -2, -2): # backwards through even elements
mo = re.search(fr"^{wordlist[i]}$", word, re.IGNORECASE)
if mo:
if wordlist[i + 1] is None:
return None
pl = DOLLAR_DIGITS.sub(
r"\\1", wordlist[i + 1]
r"\\1", cast(Word, wordlist[i + 1])
) # change $n to \n for expand
return mo.expand(pl)
return None
Expand Down Expand Up @@ -2295,7 +2299,8 @@ def _string_to_substitute(

# 0. PERFORM GENERAL INFLECTIONS IN A STRING

def inflect(self, text: str) -> str:
@validate_arguments
def inflect(self, text: Word) -> str:
"""
Perform inflections in a string.
Expand Down Expand Up @@ -2371,7 +2376,8 @@ def partition_word(self, text: str) -> Tuple[str, str, str]:
else:
return "", "", ""

def plural(self, text: str, count: Optional[Union[str, int]] = None) -> str:
@validate_arguments
def plural(self, text: Word, count: Optional[Union[str, int, Any]] = None) -> str:
"""
Return the plural of text.
Expand All @@ -2394,7 +2400,10 @@ def plural(self, text: str, count: Optional[Union[str, int]] = None) -> str:
)
return f"{pre}{plural}{post}"

def plural_noun(self, text: str, count: Optional[Union[str, int]] = None) -> str:
@validate_arguments
def plural_noun(
self, text: Word, count: Optional[Union[str, int, Any]] = None
) -> str:
"""
Return the plural of text, where text is a noun.
Expand All @@ -2412,7 +2421,10 @@ def plural_noun(self, text: str, count: Optional[Union[str, int]] = None) -> str
plural = self.postprocess(word, self._plnoun(word, count))
return f"{pre}{plural}{post}"

def plural_verb(self, text: str, count: Optional[Union[str, int]] = None) -> str:
@validate_arguments
def plural_verb(
self, text: Word, count: Optional[Union[str, int, Any]] = None
) -> str:
"""
Return the plural of text, where text is a verb.
Expand All @@ -2433,7 +2445,10 @@ def plural_verb(self, text: str, count: Optional[Union[str, int]] = None) -> str
)
return f"{pre}{plural}{post}"

def plural_adj(self, text: str, count: str = None) -> str:
@validate_arguments
def plural_adj(
self, text: Word, count: Optional[Union[str, int, Any]] = None
) -> str:
"""
Return the plural of text, where text is an adjective.
Expand Down Expand Up @@ -2530,10 +2545,11 @@ def compare_adjs(self, word1: Word, word2: Word) -> Union[str, bool]:
"""
return self._plequal(word1, word2, self.plural_adj)

@validate_arguments
def singular_noun(
self,
text: str,
count: Optional[Union[int, str]] = None,
text: Word,
count: Optional[Union[int, str, Any]] = None,
gender: Optional[str] = None,
) -> Union[str, bool]:
"""
Expand Down Expand Up @@ -3465,7 +3481,8 @@ def _sinoun( # noqa: C901

# ADJECTIVES

def a(self, text: str, count: int = 1) -> str:
@validate_arguments
def a(self, text: Word, count: Optional[Union[int, str, Any]] = 1) -> str:
"""
Return the appropriate indefinite article followed by text.
Expand All @@ -3490,7 +3507,9 @@ def a(self, text: str, count: int = 1) -> str:

an = a

def _indef_article(self, word: str, count: int) -> str: # noqa: C901
def _indef_article( # noqa: C901
self, word: str, count: Union[int, str, Any]
) -> str:
mycount = self.get_count(count)

if mycount != 1:
Expand Down Expand Up @@ -3541,7 +3560,8 @@ def _indef_article(self, word: str, count: int) -> str: # noqa: C901

# 2. TRANSLATE ZERO-QUANTIFIED $word TO "no plural($word)"

def no(self, text: str, count: Optional[Union[int, str]] = None) -> str:
@validate_arguments
def no(self, text: Word, count: Optional[Union[int, str]] = None) -> str:
"""
If count is 0, no, zero or nil, return 'no' followed by the plural
of text.
Expand Down Expand Up @@ -3578,7 +3598,8 @@ def no(self, text: str, count: Optional[Union[int, str]] = None) -> str:

# PARTICIPLES

def present_participle(self, word: str) -> str:
@validate_arguments
def present_participle(self, word: Word) -> str:
"""
Return the present participle for word.
Expand All @@ -3596,7 +3617,8 @@ def present_participle(self, word: str) -> str:

# NUMERICAL INFLECTIONS

def ordinal(self, num: Union[int, str]) -> str: # noqa: C901
@validate_arguments
def ordinal(self, num: Union[int, Word]) -> str: # noqa: C901
"""
Return the ordinal of num.
Expand Down Expand Up @@ -3755,16 +3777,17 @@ def enword(self, num: str, group: int) -> str:
num = ONE_DIGIT_WORD.sub(self.unitsub, num, 1)
return num

@validate_arguments(config=dict(arbitrary_types_allowed=True)) # noqa: C901
def number_to_words( # noqa: C901
self,
num: Union[int, str],
num: Union[Number, Word],
wantlist: bool = False,
group: int = 0,
comma: str = ",",
comma: Union[Falsish, str] = ",",
andword: str = "and",
zero: str = "zero",
one: str = "one",
decimal: str = "point",
decimal: Union[Falsish, str] = "point",
threshold: Optional[int] = None,
) -> Union[str, List[str]]:
"""
Expand Down Expand Up @@ -3906,9 +3929,10 @@ def number_to_words( # noqa: C901

# Join words with commas and a trailing 'and' (when appropriate)...

@validate_arguments
def join(
self,
words: Optional[Sequence[str]],
words: Optional[Sequence[Word]],
sep: Optional[str] = None,
sep_spaced: bool = True,
final_sep: Optional[str] = None,
Expand Down
19 changes: 12 additions & 7 deletions tests/test_pwd.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import unittest

import pytest

from inflect import (
BadChunkingOptionError,
NumOutOfRangeError,
Expand Down Expand Up @@ -253,7 +255,6 @@ def test_partition_word(self):
def test_pl(self):
p = inflect.engine()
for fn, sing, plur in (
(p.plural, "", ""),
(p.plural, "cow", "cows"),
(p.plural, "thought", "thoughts"),
(p.plural, "mouse", "mice"),
Expand All @@ -265,15 +266,12 @@ def test_pl(self):
(p.plural, "carmen", "carmina"),
(p.plural, "quartz", "quartzes"),
(p.plural, "care", "cares"),
(p.plural_noun, "", ""),
(p.plural_noun, "cow", "cows"),
(p.plural_noun, "thought", "thoughts"),
(p.plural_noun, "snooze", "snoozes"),
(p.plural_verb, "", ""),
(p.plural_verb, "runs", "run"),
(p.plural_verb, "thought", "thought"),
(p.plural_verb, "eyes", "eye"),
(p.plural_adj, "", ""),
(p.plural_adj, "a", "some"),
(p.plural_adj, "this", "these"),
(p.plural_adj, "that", "those"),
Expand Down Expand Up @@ -313,6 +311,12 @@ def test_pl(self):
self.assertEqual(p.plural("die"), "dice")
self.assertEqual(p.plural_noun("die"), "dice")

with pytest.raises(Exception):
p.plural("")
p.plural_noun("")
p.plural_verb("")
p.plural_adj("")

def test_sinoun(self):
p = inflect.engine()
for sing, plur in (
Expand Down Expand Up @@ -567,7 +571,6 @@ def test_count(self):
def test__plnoun(self):
p = inflect.engine()
for sing, plur in (
("", ""),
("tuna", "tuna"),
("TUNA", "TUNA"),
("swordfish", "swordfish"),
Expand Down Expand Up @@ -698,7 +701,8 @@ def test_classical_pl(self):

def test__pl_special_verb(self):
p = inflect.engine()
self.assertEqual(p._pl_special_verb(""), False)
with pytest.raises(Exception):
self.assertEqual(p._pl_special_verb(""), False)
self.assertEqual(p._pl_special_verb("am"), "are")
self.assertEqual(p._pl_special_verb("am", 0), "are")
self.assertEqual(p._pl_special_verb("runs", 0), "run")
Expand Down Expand Up @@ -800,7 +804,8 @@ def test_a(self):
self.assertEqual(p.a("cat", 2), "2 cat")

self.assertEqual(p.a, p.an)
self.assertEqual(p.a(""), "")
with pytest.raises(Exception):
p.a("")

def test_no(self):
p = inflect.engine()
Expand Down

0 comments on commit 77e7587

Please sign in to comment.