Skip to content

Handle negative numbers in ordinal() (#192) - #259

Open
apoorvdarshan wants to merge 1 commit into
jaraco:mainfrom
apoorvdarshan:ordinal-negative-numbers-192
Open

Handle negative numbers in ordinal() (#192)#259
apoorvdarshan wants to merge 1 commit into
jaraco:mainfrom
apoorvdarshan:ordinal-negative-numbers-192

Conversation

@apoorvdarshan

Copy link
Copy Markdown

Addresses the negative-number cases of #192.

Problem

ordinal() mishandles negative numbers:

>>> p = inflect.engine()
>>> p.ordinal(-1)      # TypeError: expected string or bytes-like object, got 'int'
>>> p.ordinal("-1")    # '-1th'   (should be '-1st')
>>> p.ordinal("-21")   # '-21th'  (should be '-21st')

ordinal() uses DIGIT.match(str(num)) to decide whether the input is numeric, but DIGIT (\d) does not match a leading -. So negative numbers fall through to _sub_ord, which is meant for word input (e.g. "one""first"). For a negative string it just appends "th"; for a negative int it runs re.sub on the integer and raises TypeError.

Fix

  • Accept an optional leading - in the numeric branch.
  • Base the suffix on abs(n)-1 % 10 is 9 in Python, which would give the wrong suffix.
>>> p.ordinal(-1)      # '-1st'
>>> p.ordinal(-2)      # '-2nd'
>>> p.ordinal(-113)    # '-113th'
>>> p.ordinal("-21")   # '-21st'

Positive numbers, floats, and word inputs are unchanged (e.g. ordinal(1)'1st', ordinal(1.5)'1.5th', ordinal("one")'first').

This addresses the negative-number cases raised in #192. I left the ordinal(1.0)'1.0st' question alone, since whether that should be '1st', '1.0th', or unchanged is a design call for you rather than a clear defect.

Testing

  • Added test_negative_ordinals to tests/test_inflections.py (ints and strings, including the teens which correctly keep "th"). It raises TypeError on main and passes with this change.
  • Full test suite: 215 passed, 16 xfailed — no regressions. Doctests pass. ruff format clean and the change introduces no new ruff check findings.
  • Added a newsfragments/192.bugfix.rst.

Disclosure: this change was prepared with the assistance of an AI tool (Claude Code). I reproduced the issue, implemented and verified the fix and tests, ran the suite and linters, and take responsibility for the contribution and will respond to review feedback personally.

ordinal() raised a TypeError for negative integers (they fell through to
_sub_ord, which runs re.sub on the value) and returned a bare 'th' suffix
for negative strings (e.g. '-1th'), because DIGIT.match() does not match a
leading '-'.

Accept an optional leading '-' and base the suffix on abs(n) (since
'-1 % 10' is 9 in Python), so ordinal(-1) is '-1st', ordinal(-113) is
'-113th', etc. Positive and word inputs are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant