Opened 5 years ago

Last modified 5 years ago

#20122 assigned Cleanup/optimization

Pluralize filter sometimes returns singular form instead of an empty string for invalid inputs

Reported by: Aymeric Augustin Owned by: Jorge C. Leitão
Component: Template system Version: master
Severity: Normal Keywords:
Cc: Jorge C. Leitão Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: yes Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

Filters are documented to return either their input unchanged, or the empty string, whatever makes most sense, when they're used incorrectly. The pluralize filter returns the empty string in such cases, for instance when it receives more than two forms (singular, plural).

However, it returns the singular form instead of the empty string when it's passed an object that isn't a number, a string or a list.

Failing test case:

--- a/tests/defaultfilters/tests.py
+++ b/tests/defaultfilters/tests.py
@@ -622,6 +622,9 @@ class DefaultFiltersTests(TestCase):
         self.assertEqual(pluralize(2,'y,ies'), 'ies')
         self.assertEqual(pluralize(0,'y,ies,error'), '')
 
+    def test_pluralize_error(self):
+        self.assertEqual(pluralize(object, 'y,ies'), '')
+
     def test_phone2numeric(self):
         self.assertEqual(phone2numeric_filter('0800 flowers'), '0800 3569377')
 

I understand that the implementation is crafted to avoid isinstance checks, but in this case we really want different logic depending on the type of the input. I think the filter should be rewritten with the following pseudo-code:

if the value is a number:
    return singular if value is 1 else plural
if the value is a string:
    return singular if value is '1' else plural
if the value has a length (needs a try/except TypeError):
    return singular if length is 1 else plural
return ''

I discovered this while working on #16723.

Attachments (1)

defaultfilters_fix_pluralize.diff (1.8 KB) - added by Jorge C. Leitão 5 years ago.

Download all attachments as: .zip

Change History (7)

comment:1 Changed 5 years ago by Claude Paroz

Triage Stage: UnreviewedAccepted

comment:2 Changed 5 years ago by Jorge C. Leitão

I don't agree with the suggested implementation because when you pass a string to a pluralize it should not assume the string can be "pluralized". In your case, if you pass 'one', it would return the plural, and I think that it should return an empty string: it is language dependent whether strings represent singular or plural number.

One possible solution that compromises both cases:

if value == 1 or value == u'1':
    return singular
try:
    float(value)
    return plural
except ValueError:
   # a string that cannot be parsed as a float and thus is an undefined number 
   return ''

This avoids the isinstance() and also corrects the pluralization of floats (1 unit but 1.1 units).

Cheers,
Jorge

comment:3 Changed 5 years ago by Jorge C. Leitão

Cc: Jorge C. Leitão added
Owner: changed from nobody to Jorge C. Leitão
Status: newassigned

comment:4 Changed 5 years ago by Jorge C. Leitão

The added patch provides the following functionality:

if value is 1 or '1' returns singular
elif it is a string representable by a float returns plural
elif is something that can be argument of len():
   if len(value) == 1: returns singular
   else: returns plural
else, returns ''

This complements the existing functionality, extends to float numbers, and fixes this ticket, returning an empty string in some case where a number (plural/singular) cannot be defined by the value.

Last edited 5 years ago by Jorge C. Leitão (previous) (diff)

Changed 5 years ago by Jorge C. Leitão

comment:5 Changed 5 years ago by Jorge C. Leitão

Has patch: set

comment:6 Changed 5 years ago by Łukasz Rekucki

Needs tests: set
Patch needs improvement: set
Note: See TracTickets for help on using tickets.
Back to Top