Issue #5812: make Fraction('1e-6') valid. Backport of r71806.

This commit is contained in:
Mark Dickinson 2009-04-22 18:15:25 +00:00
parent ebafbb705c
commit 8100bd8431
4 changed files with 46 additions and 32 deletions

View File

@ -27,20 +27,17 @@ another rational number, or from a string.
*other_fraction* is an instance of :class:`numbers.Rational` and *other_fraction* is an instance of :class:`numbers.Rational` and
returns an :class:`Fraction` instance with the same value. The returns an :class:`Fraction` instance with the same value. The
last version of the constructor expects a string or unicode last version of the constructor expects a string or unicode
instance in one of two possible forms. The first form is:: instance. The usual form for this instance is::
[sign] numerator ['/' denominator] [sign] numerator ['/' denominator]
where the optional ``sign`` may be either '+' or '-' and where the optional ``sign`` may be either '+' or '-' and
``numerator`` and ``denominator`` (if present) are strings of ``numerator`` and ``denominator`` (if present) are strings of
decimal digits. The second permitted form is that of a number decimal digits. In addition, any string that represents a finite
containing a decimal point:: value and is accepted by the :class:`float` constructor is also
accepted by the :class:`Fraction` constructor. In either form the
[sign] integer '.' [fraction] | [sign] '.' fraction input string may also have leading and/or trailing whitespace.
Here are some examples::
where ``integer`` and ``fraction`` are strings of digits. In
either form the input string may also have leading and/or trailing
whitespace. Here are some examples::
>>> from fractions import Fraction >>> from fractions import Fraction
>>> Fraction(16, -10) >>> Fraction(16, -10)
@ -58,6 +55,8 @@ another rational number, or from a string.
Fraction(1414213, 1000000) Fraction(1414213, 1000000)
>>> Fraction('-.125') >>> Fraction('-.125')
Fraction(-1, 8) Fraction(-1, 8)
>>> Fraction('7e-6')
Fraction(7, 1000000)
The :class:`Fraction` class inherits from the abstract base class The :class:`Fraction` class inherits from the abstract base class

View File

@ -30,13 +30,14 @@ _RATIONAL_FORMAT = re.compile(r"""
(?P<sign>[-+]?) # an optional sign, then (?P<sign>[-+]?) # an optional sign, then
(?=\d|\.\d) # lookahead for digit or .digit (?=\d|\.\d) # lookahead for digit or .digit
(?P<num>\d*) # numerator (possibly empty) (?P<num>\d*) # numerator (possibly empty)
(?: # followed by an optional (?: # followed by
/(?P<denom>\d+) # / and denominator (?:/(?P<denom>\d+))? # an optional denominator
| # or | # or
\.(?P<decimal>\d*) # decimal point and fractional part (?:\.(?P<decimal>\d*))? # an optional fractional part
)? (?:E(?P<exp>[-+]?\d+))? # and optional exponent
)
\s*\Z # and optional whitespace to finish \s*\Z # and optional whitespace to finish
""", re.VERBOSE) """, re.VERBOSE | re.IGNORECASE)
class Fraction(Rational): class Fraction(Rational):
@ -67,22 +68,28 @@ class Fraction(Rational):
if type(numerator) not in (int, long) and denominator == 1: if type(numerator) not in (int, long) and denominator == 1:
if isinstance(numerator, basestring): if isinstance(numerator, basestring):
# Handle construction from strings. # Handle construction from strings.
input = numerator m = _RATIONAL_FORMAT.match(numerator)
m = _RATIONAL_FORMAT.match(input)
if m is None: if m is None:
raise ValueError('Invalid literal for Fraction: %r' % input) raise ValueError('Invalid literal for Fraction: %r' %
numerator = m.group('num') numerator)
numerator = int(m.group('num') or '0')
denom = m.group('denom')
if denom:
denominator = int(denom)
else:
denominator = 1
decimal = m.group('decimal') decimal = m.group('decimal')
if decimal: if decimal:
# The literal is a decimal number. scale = 10**len(decimal)
numerator = int(numerator + decimal) numerator = numerator * scale + int(decimal)
denominator = 10**len(decimal) denominator *= scale
exp = m.group('exp')
if exp:
exp = int(exp)
if exp >= 0:
numerator *= 10**exp
else: else:
# The literal is an integer or fraction. denominator *= 10**-exp
numerator = int(numerator)
# Default denominator to 1.
denominator = int(m.group('denom') or 1)
if m.group('sign') == '-': if m.group('sign') == '-':
numerator = -numerator numerator = -numerator

View File

@ -80,6 +80,11 @@ class FractionTest(unittest.TestCase):
self.assertEquals((-16, 5), _components(F(u" -3.2 "))) self.assertEquals((-16, 5), _components(F(u" -3.2 ")))
self.assertEquals((-3, 1), _components(F(u" -3. "))) self.assertEquals((-3, 1), _components(F(u" -3. ")))
self.assertEquals((3, 5), _components(F(u" .6 "))) self.assertEquals((3, 5), _components(F(u" .6 ")))
self.assertEquals((1, 3125), _components(F("32.e-5")))
self.assertEquals((1000000, 1), _components(F("1E+06")))
self.assertEquals((-12300, 1), _components(F("-1.23e4")))
self.assertEquals((0, 1), _components(F(" .0e+0\t")))
self.assertEquals((0, 1), _components(F("-0.000e0")))
self.assertRaisesMessage( self.assertRaisesMessage(
@ -88,6 +93,9 @@ class FractionTest(unittest.TestCase):
self.assertRaisesMessage( self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '3/'", ValueError, "Invalid literal for Fraction: '3/'",
F, "3/") F, "3/")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '/2'",
F, "/2")
self.assertRaisesMessage( self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '3 /2'", ValueError, "Invalid literal for Fraction: '3 /2'",
F, "3 /2") F, "3 /2")
@ -103,10 +111,6 @@ class FractionTest(unittest.TestCase):
# Avoid treating '.' as a regex special character. # Avoid treating '.' as a regex special character.
ValueError, "Invalid literal for Fraction: '3a2'", ValueError, "Invalid literal for Fraction: '3a2'",
F, "3a2") F, "3a2")
self.assertRaisesMessage(
# Only parse ordinary decimals, not scientific form.
ValueError, "Invalid literal for Fraction: '3.2e4'",
F, "3.2e4")
self.assertRaisesMessage( self.assertRaisesMessage(
# Don't accept combinations of decimals and fractions. # Don't accept combinations of decimals and fractions.
ValueError, "Invalid literal for Fraction: '3/7.2'", ValueError, "Invalid literal for Fraction: '3/7.2'",

View File

@ -241,6 +241,10 @@ Core and Builtins
Library Library
------- -------
- Issue #5812: Fraction('1e6') is valid: more generally, any string
that's valid for float() is now valid for Fraction(), with the
exception of strings representing NaNs and infinities.
- Issue #5795: Fixed test_distutils failure on Debian ppc. - Issue #5795: Fixed test_distutils failure on Debian ppc.
- Issue #5768: Fixed bug in Unicode output logic and test case for same. - Issue #5768: Fixed bug in Unicode output logic and test case for same.