Issue #5812: Make Fraction('1e6') valid. The Fraction constructor now
accepts all strings accepted by the float and Decimal constructors, with the exception of strings representing NaNs or infinities.
This commit is contained in:
parent
937491d1a9
commit
cf63f2fb88
|
@ -25,21 +25,18 @@ another rational number, or from a string.
|
|||
:exc:`ZeroDivisionError`. The second version requires that
|
||||
*other_fraction* is an instance of :class:`numbers.Rational` and
|
||||
returns an :class:`Fraction` instance with the same value. The
|
||||
last version of the constructor expects a string
|
||||
instance in one of two possible forms. The first form is::
|
||||
last version of the constructor expects a string instance. The
|
||||
usual form for this string is::
|
||||
|
||||
[sign] numerator ['/' denominator]
|
||||
|
||||
where the optional ``sign`` may be either '+' or '-' and
|
||||
``numerator`` and ``denominator`` (if present) are strings of
|
||||
decimal digits. The second permitted form is that of a number
|
||||
containing a decimal point::
|
||||
|
||||
[sign] integer '.' [fraction] | [sign] '.' fraction
|
||||
|
||||
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::
|
||||
decimal digits. In addition, any string that represents a finite
|
||||
value and is accepted by the :class:`float` constructor is also
|
||||
accepted by the :class:`Fraction` constructor. In either form the
|
||||
input string may also have leading and/or trailing whitespace.
|
||||
Here are some examples::
|
||||
|
||||
>>> from fractions import Fraction
|
||||
>>> Fraction(16, -10)
|
||||
|
@ -57,6 +54,8 @@ another rational number, or from a string.
|
|||
Fraction(1414213, 1000000)
|
||||
>>> Fraction('-.125')
|
||||
Fraction(-1, 8)
|
||||
>>> Fraction('7e-6')
|
||||
Fraction(7, 1000000)
|
||||
|
||||
|
||||
The :class:`Fraction` class inherits from the abstract base class
|
||||
|
|
|
@ -28,13 +28,14 @@ _RATIONAL_FORMAT = re.compile(r"""
|
|||
(?P<sign>[-+]?) # an optional sign, then
|
||||
(?=\d|\.\d) # lookahead for digit or .digit
|
||||
(?P<num>\d*) # numerator (possibly empty)
|
||||
(?: # followed by an optional
|
||||
/(?P<denom>\d+) # / and denominator
|
||||
(?: # followed by
|
||||
(?:/(?P<denom>\d+))? # an optional denominator
|
||||
| # 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
|
||||
""", re.VERBOSE)
|
||||
""", re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
|
||||
class Fraction(numbers.Rational):
|
||||
|
@ -65,22 +66,28 @@ class Fraction(numbers.Rational):
|
|||
if not isinstance(numerator, int) and denominator == 1:
|
||||
if isinstance(numerator, str):
|
||||
# Handle construction from strings.
|
||||
input = numerator
|
||||
m = _RATIONAL_FORMAT.match(input)
|
||||
m = _RATIONAL_FORMAT.match(numerator)
|
||||
if m is None:
|
||||
raise ValueError('Invalid literal for Fraction: %r' % input)
|
||||
numerator = m.group('num')
|
||||
decimal = m.group('decimal')
|
||||
if decimal:
|
||||
# The literal is a decimal number.
|
||||
numerator = int(numerator + decimal)
|
||||
denominator = 10**len(decimal)
|
||||
raise ValueError('Invalid literal for Fraction: %r' %
|
||||
numerator)
|
||||
numerator = int(m.group('num') or '0')
|
||||
denom = m.group('denom')
|
||||
if denom:
|
||||
denominator = int(denom)
|
||||
else:
|
||||
# The literal is an integer or fraction.
|
||||
numerator = int(numerator)
|
||||
# Default denominator to 1.
|
||||
denominator = int(m.group('denom') or 1)
|
||||
|
||||
denominator = 1
|
||||
decimal = m.group('decimal')
|
||||
if decimal:
|
||||
scale = 10**len(decimal)
|
||||
numerator = numerator * scale + int(decimal)
|
||||
denominator *= scale
|
||||
exp = m.group('exp')
|
||||
if exp:
|
||||
exp = int(exp)
|
||||
if exp >= 0:
|
||||
numerator *= 10**exp
|
||||
else:
|
||||
denominator *= 10**-exp
|
||||
if m.group('sign') == '-':
|
||||
numerator = -numerator
|
||||
|
||||
|
|
|
@ -78,6 +78,11 @@ class FractionTest(unittest.TestCase):
|
|||
self.assertEquals((-16, 5), _components(F(" -3.2 ")))
|
||||
self.assertEquals((-3, 1), _components(F(" -3. ")))
|
||||
self.assertEquals((3, 5), _components(F(" .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(
|
||||
ZeroDivisionError, "Fraction(3, 0)",
|
||||
|
@ -85,6 +90,9 @@ class FractionTest(unittest.TestCase):
|
|||
self.assertRaisesMessage(
|
||||
ValueError, "Invalid literal for Fraction: '3/'",
|
||||
F, "3/")
|
||||
self.assertRaisesMessage(
|
||||
ValueError, "Invalid literal for Fraction: '/2'",
|
||||
F, "/2")
|
||||
self.assertRaisesMessage(
|
||||
ValueError, "Invalid literal for Fraction: '3 /2'",
|
||||
F, "3 /2")
|
||||
|
@ -100,10 +108,6 @@ class FractionTest(unittest.TestCase):
|
|||
# Avoid treating '.' as a regex special character.
|
||||
ValueError, "Invalid literal for Fraction: '3a2'",
|
||||
F, "3a2")
|
||||
self.assertRaisesMessage(
|
||||
# Only parse ordinary decimals, not scientific form.
|
||||
ValueError, "Invalid literal for Fraction: '3.2e4'",
|
||||
F, "3.2e4")
|
||||
self.assertRaisesMessage(
|
||||
# Don't accept combinations of decimals and rationals.
|
||||
ValueError, "Invalid literal for Fraction: '3/7.2'",
|
||||
|
|
|
@ -75,6 +75,10 @@ Core and Builtins
|
|||
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 #5734: BufferedRWPair was poorly tested and had several glaring
|
||||
bugs. Patch by Brian Quinlan.
|
||||
|
||||
|
|
Loading…
Reference in New Issue