mirror of
https://github.com/python/cpython.git
synced 2025-09-27 10:50:04 +00:00
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
4 changed files with 47 additions and 33 deletions
|
@ -25,21 +25,18 @@ another rational number, or from a string.
|
||||||
:exc:`ZeroDivisionError`. The second version requires that
|
:exc:`ZeroDivisionError`. The second version requires that
|
||||||
*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
|
last version of the constructor expects a string instance. The
|
||||||
instance in one of two possible forms. The first form is::
|
usual form for this string 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)
|
||||||
|
@ -57,6 +54,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
|
||||||
|
|
|
@ -28,13 +28,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(numbers.Rational):
|
class Fraction(numbers.Rational):
|
||||||
|
@ -65,22 +66,28 @@ class Fraction(numbers.Rational):
|
||||||
if not isinstance(numerator, int) and denominator == 1:
|
if not isinstance(numerator, int) and denominator == 1:
|
||||||
if isinstance(numerator, str):
|
if isinstance(numerator, str):
|
||||||
# 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)
|
||||||
decimal = m.group('decimal')
|
numerator = int(m.group('num') or '0')
|
||||||
if decimal:
|
denom = m.group('denom')
|
||||||
# The literal is a decimal number.
|
if denom:
|
||||||
numerator = int(numerator + decimal)
|
denominator = int(denom)
|
||||||
denominator = 10**len(decimal)
|
|
||||||
else:
|
else:
|
||||||
# The literal is an integer or fraction.
|
denominator = 1
|
||||||
numerator = int(numerator)
|
decimal = m.group('decimal')
|
||||||
# Default denominator to 1.
|
if decimal:
|
||||||
denominator = int(m.group('denom') or 1)
|
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') == '-':
|
if m.group('sign') == '-':
|
||||||
numerator = -numerator
|
numerator = -numerator
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,11 @@ class FractionTest(unittest.TestCase):
|
||||||
self.assertEquals((-16, 5), _components(F(" -3.2 ")))
|
self.assertEquals((-16, 5), _components(F(" -3.2 ")))
|
||||||
self.assertEquals((-3, 1), _components(F(" -3. ")))
|
self.assertEquals((-3, 1), _components(F(" -3. ")))
|
||||||
self.assertEquals((3, 5), _components(F(" .6 ")))
|
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(
|
self.assertRaisesMessage(
|
||||||
ZeroDivisionError, "Fraction(3, 0)",
|
ZeroDivisionError, "Fraction(3, 0)",
|
||||||
|
@ -85,6 +90,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")
|
||||||
|
@ -100,10 +108,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 rationals.
|
# Don't accept combinations of decimals and rationals.
|
||||||
ValueError, "Invalid literal for Fraction: '3/7.2'",
|
ValueError, "Invalid literal for Fraction: '3/7.2'",
|
||||||
|
|
|
@ -75,6 +75,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 #5734: BufferedRWPair was poorly tested and had several glaring
|
- Issue #5734: BufferedRWPair was poorly tested and had several glaring
|
||||||
bugs. Patch by Brian Quinlan.
|
bugs. Patch by Brian Quinlan.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue