Update the fractions and ftplib libraries + associated tests - v.3.13.10 (#6607)

* Updated ftplib + test library

* Updated fractions + test library
This commit is contained in:
Terry Tianlin Luan authored and GitHub committed 2026-01-03 08:28:25 +09:00
1 parent 52dd8292c4
commit 5ae75a679b
4 files changed
+338 -197

No files matched your search

+81 -26
View File
@@ -139,6 +139,23 @@ def _round_to_figures(n, d, figures):
return sign, significand, exponent
# Pattern for matching non-float-style format specifications.
_GENERAL_FORMAT_SPECIFICATION_MATCHER = re.compile(r"""
(?:
(?P<fill>.)?
(?P<align>[<>=^])
)?
(?P<sign>[-+ ]?)
# Alt flag forces a slash and denominator in the output, even for
# integer-valued Fraction objects.
(?P<alt>\#)?
# We don't implement the zeropad flag since there's no single obvious way
# to interpret it.
(?P<minimumwidth>0|[1-9][0-9]*)?
(?P<thousands_sep>[,_])?
""", re.DOTALL | re.VERBOSE).fullmatch
# Pattern for matching float-style format specifications;
# supports 'e', 'E', 'f', 'F', 'g', 'G' and '%' presentation types.
_FLOAT_FORMAT_SPECIFICATION_MATCHER = re.compile(r"""
@@ -414,27 +431,42 @@ class Fraction(numbers.Rational):
else:
return '%s/%s' % (self._numerator, self._denominator)
def __format__(self, format_spec, /):
"""Format this fraction according to the given format specification."""
# Backwards compatiblility with existing formatting.
if not format_spec:
return str(self)
def _format_general(self, match):
"""Helper method for __format__.
Handles fill, alignment, signs, and thousands separators in the
case of no presentation type.
"""
# Validate and parse the format specifier.
match = _FLOAT_FORMAT_SPECIFICATION_MATCHER(format_spec)
if match is None:
raise ValueError(
f"Invalid format specifier {format_spec!r} "
f"for object of type {type(self).__name__!r}"
)
elif match["align"] is not None and match["zeropad"] is not None:
# Avoid the temptation to guess.
raise ValueError(
f"Invalid format specifier {format_spec!r} "
f"for object of type {type(self).__name__!r}; "
"can't use explicit alignment when zero-padding"
)
fill = match["fill"] or " "
align = match["align"] or ">"
pos_sign = "" if match["sign"] == "-" else match["sign"]
alternate_form = bool(match["alt"])
minimumwidth = int(match["minimumwidth"] or "0")
thousands_sep = match["thousands_sep"] or ''
# Determine the body and sign representation.
n, d = self._numerator, self._denominator
if d > 1 or alternate_form:
body = f"{abs(n):{thousands_sep}}/{d:{thousands_sep}}"
else:
body = f"{abs(n):{thousands_sep}}"
sign = '-' if n < 0 else pos_sign
# Pad with fill character if necessary and return.
padding = fill * (minimumwidth - len(sign) - len(body))
if align == ">":
return padding + sign + body
elif align == "<":
return sign + body + padding
elif align == "^":
half = len(padding) // 2
return padding[:half] + sign + body + padding[half:]
else: # align == "="
return sign + padding + body
def _format_float_style(self, match):
"""Helper method for __format__; handles float presentation types."""
fill = match["fill"] or " "
align = match["align"] or ">"
pos_sign = "" if match["sign"] == "-" else match["sign"]
@@ -449,6 +481,9 @@ class Fraction(numbers.Rational):
trim_point = not alternate_form
exponent_indicator = "E" if presentation_type in "EFG" else "e"
if align == '=' and fill == '0':
zeropad = True
# Round to get the digits we need, figure out where to place the point,
# and decide whether to use scientific notation. 'point_pos' is the
# relative to the _end_ of the digit string: that is, it's the number
@@ -530,7 +565,25 @@ class Fraction(numbers.Rational):
else: # align == "="
return sign + padding + body
def _operator_fallbacks(monomorphic_operator, fallback_operator):
def __format__(self, format_spec, /):
"""Format this fraction according to the given format specification."""
if match := _GENERAL_FORMAT_SPECIFICATION_MATCHER(format_spec):
return self._format_general(match)
if match := _FLOAT_FORMAT_SPECIFICATION_MATCHER(format_spec):
# Refuse the temptation to guess if both alignment _and_
# zero padding are specified.
if match["align"] is None or match["zeropad"] is None:
return self._format_float_style(match)
raise ValueError(
f"Invalid format specifier {format_spec!r} "
f"for object of type {type(self).__name__!r}"
)
def _operator_fallbacks(monomorphic_operator, fallback_operator,
handle_complex=True):
"""Generates forward and reverse operators given a purely-rational
operator and a function from the operator module.
@@ -617,7 +670,7 @@ class Fraction(numbers.Rational):
return monomorphic_operator(a, Fraction(b))
elif isinstance(b, float):
return fallback_operator(float(a), b)
elif isinstance(b, complex):
elif handle_complex and isinstance(b, complex):
return fallback_operator(complex(a), b)
else:
return NotImplemented
@@ -630,7 +683,7 @@ class Fraction(numbers.Rational):
return monomorphic_operator(Fraction(a), b)
elif isinstance(a, numbers.Real):
return fallback_operator(float(a), float(b))
elif isinstance(a, numbers.Complex):
elif handle_complex and isinstance(a, numbers.Complex):
return fallback_operator(complex(a), complex(b))
else:
return NotImplemented
@@ -781,7 +834,7 @@ class Fraction(numbers.Rational):
"""a // b"""
return (a.numerator * b.denominator) // (a.denominator * b.numerator)
__floordiv__, __rfloordiv__ = _operator_fallbacks(_floordiv, operator.floordiv)
__floordiv__, __rfloordiv__ = _operator_fallbacks(_floordiv, operator.floordiv, False)
def _divmod(a, b):
"""(a // b, a % b)"""
@@ -789,14 +842,14 @@ class Fraction(numbers.Rational):
div, n_mod = divmod(a.numerator * db, da * b.numerator)
return div, Fraction(n_mod, da * db)
__divmod__, __rdivmod__ = _operator_fallbacks(_divmod, divmod)
__divmod__, __rdivmod__ = _operator_fallbacks(_divmod, divmod, False)
def _mod(a, b):
"""a % b"""
da, db = a.denominator, b.denominator
return Fraction((a.numerator * db) % (b.numerator * da), da * db)
__mod__, __rmod__ = _operator_fallbacks(_mod, operator.mod)
__mod__, __rmod__ = _operator_fallbacks(_mod, operator.mod, False)
def __pow__(a, b):
"""a ** b
@@ -825,8 +878,10 @@ class Fraction(numbers.Rational):
# A fractional power will generally produce an
# irrational number.
return float(a) ** float(b)
else:
elif isinstance(b, (float, complex)):
return float(a) ** b
else:
return NotImplemented
def __rpow__(b, a):
"""a ** b"""
+17 -10
View File
@@ -900,11 +900,17 @@ def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
def test():
'''Test program.
Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
Usage: ftplib [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
-d dir
-l list
-p password
Options:
-d increase debugging level
-r[file] set alternate ~/.netrc file
Commands:
-l[dir] list directory
-d[dir] change the current directory
-p toggle passive and active mode
file retrieve the file and write it to stdout
'''
if len(sys.argv) < 2:
@@ -930,15 +936,14 @@ def test():
netrcobj = netrc.netrc(rcfile)
except OSError:
if rcfile is not None:
sys.stderr.write("Could not open account file"
" -- using anonymous login.")
print("Could not open account file -- using anonymous login.",
file=sys.stderr)
else:
try:
userid, acct, passwd = netrcobj.authenticators(host)
except KeyError:
except (KeyError, TypeError):
# no account for host
sys.stderr.write(
"No account -- using anonymous login.")
print("No account -- using anonymous login.", file=sys.stderr)
ftp.login(userid, passwd, acct)
for file in sys.argv[2:]:
if file[:2] == '-l':
@@ -951,7 +956,9 @@ def test():
ftp.set_pasv(not ftp.passiveserver)
else:
ftp.retrbinary('RETR ' + file, \
sys.stdout.write, 1024)
sys.stdout.buffer.write, 1024)
sys.stdout.buffer.flush()
sys.stdout.flush()
ftp.quit()
+186 -129
View File
@@ -2,7 +2,7 @@
import cmath
from decimal import Decimal
# from test.support import requires_IEEE_754
from test.support import requires_IEEE_754, adjust_int_max_str_digits
import math
import numbers
import operator
@@ -19,7 +19,7 @@ F = fractions.Fraction
#locate file with float format test values
test_dir = os.path.dirname(__file__) or os.curdir
format_testfile = os.path.join(test_dir, 'formatfloat_testcases.txt')
format_testfile = os.path.join(test_dir, 'mathdata', 'formatfloat_testcases.txt')
class DummyFloat(object):
"""Dummy float class for testing comparisons with Fractions"""
@@ -97,7 +97,7 @@ def typed_approx_eq(a, b):
class Symbolic:
"""Simple non-numeric class for testing mixed arithmetic.
It is not Integral, Rational, Real or Complex, and cannot be conveted
It is not Integral, Rational, Real or Complex, and cannot be converted
to int, float or complex. but it supports some arithmetic operations.
"""
def __init__(self, value):
@@ -331,7 +331,7 @@ class FractionTest(unittest.TestCase):
self.assertRaises(TypeError, F, 3, 1j)
self.assertRaises(TypeError, F, 1, 2, 3)
# @requires_IEEE_754
@requires_IEEE_754
def testInitFromFloat(self):
self.assertEqual((5, 2), _components(F(2.5)))
self.assertEqual((0, 1), _components(F(-0.0)))
@@ -357,12 +357,14 @@ class FractionTest(unittest.TestCase):
def testFromString(self):
self.assertEqual((5, 1), _components(F("5")))
self.assertEqual((5, 1), _components(F("005")))
self.assertEqual((3, 2), _components(F("3/2")))
self.assertEqual((3, 2), _components(F("3 / 2")))
self.assertEqual((3, 2), _components(F(" \n +3/2")))
self.assertEqual((-3, 2), _components(F("-3/2 ")))
self.assertEqual((13, 2), _components(F(" 013/02 \n ")))
self.assertEqual((13, 2), _components(F(" 0013/002 \n ")))
self.assertEqual((16, 5), _components(F(" 3.2 ")))
self.assertEqual((16, 5), _components(F("003.2")))
self.assertEqual((-16, 5), _components(F(" -3.2 ")))
self.assertEqual((-3, 1), _components(F(" -3. ")))
self.assertEqual((3, 5), _components(F(" .6 ")))
@@ -381,116 +383,102 @@ class FractionTest(unittest.TestCase):
self.assertRaisesMessage(
ZeroDivisionError, "Fraction(3, 0)",
F, "3/0")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '3/'",
F, "3/")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '/2'",
F, "/2")
self.assertRaisesMessage(
# Denominators don't need a sign.
ValueError, "Invalid literal for Fraction: '3/+2'",
F, "3/+2")
self.assertRaisesMessage(
# Imitate float's parsing.
ValueError, "Invalid literal for Fraction: '+ 3/2'",
F, "+ 3/2")
self.assertRaisesMessage(
# Avoid treating '.' as a regex special character.
ValueError, "Invalid literal for Fraction: '3a2'",
F, "3a2")
self.assertRaisesMessage(
# Don't accept combinations of decimals and rationals.
ValueError, "Invalid literal for Fraction: '3/7.2'",
F, "3/7.2")
self.assertRaisesMessage(
# Don't accept combinations of decimals and rationals.
ValueError, "Invalid literal for Fraction: '3.2/7'",
F, "3.2/7")
self.assertRaisesMessage(
# Allow 3. and .3, but not .
ValueError, "Invalid literal for Fraction: '.'",
F, ".")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '_'",
F, "_")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '_1'",
F, "_1")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1__2'",
F, "1__2")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '/_'",
F, "/_")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1_/'",
F, "1_/")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '_1/'",
F, "_1/")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1__2/'",
F, "1__2/")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1/_'",
F, "1/_")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1/_1'",
F, "1/_1")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1/1__2'",
F, "1/1__2")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1._111'",
F, "1._111")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1.1__1'",
F, "1.1__1")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1.1e+_1'",
F, "1.1e+_1")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1.1e+1__1'",
F, "1.1e+1__1")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '123.dd'",
F, "123.dd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '123.5_dd'",
F, "123.5_dd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: 'dd.5'",
F, "dd.5")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '7_dd'",
F, "7_dd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1/dd'",
F, "1/dd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1/123_dd'",
F, "1/123_dd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '789edd'",
F, "789edd")
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '789e2_dd'",
F, "789e2_dd")
def check_invalid(s):
msg = "Invalid literal for Fraction: " + repr(s)
self.assertRaisesMessage(ValueError, msg, F, s)
check_invalid("3/")
check_invalid("/2")
# Denominators don't need a sign.
check_invalid("3/+2")
check_invalid("3/-2")
# Imitate float's parsing.
check_invalid("+ 3/2")
check_invalid("- 3/2")
# Avoid treating '.' as a regex special character.
check_invalid("3a2")
# Don't accept combinations of decimals and rationals.
check_invalid("3/7.2")
check_invalid("3.2/7")
# No space around dot.
check_invalid("3 .2")
check_invalid("3. 2")
# No space around e.
check_invalid("3.2 e1")
check_invalid("3.2e 1")
# Fractional part don't need a sign.
check_invalid("3.+2")
check_invalid("3.-2")
# Only accept base 10.
check_invalid("0x10")
check_invalid("0x10/1")
check_invalid("1/0x10")
check_invalid("0x10.")
check_invalid("0x10.1")
check_invalid("1.0x10")
check_invalid("1.0e0x10")
# Only accept decimal digits.
check_invalid("³")
check_invalid("³/2")
check_invalid("3/²")
check_invalid("³.2")
check_invalid("3.²")
check_invalid("3.2e²")
check_invalid("¼")
# Allow 3. and .3, but not .
check_invalid(".")
check_invalid("_")
check_invalid("_1")
check_invalid("1__2")
check_invalid("/_")
check_invalid("1_/")
check_invalid("_1/")
check_invalid("1__2/")
check_invalid("1/_")
check_invalid("1/_1")
check_invalid("1/1__2")
check_invalid("1._111")
check_invalid("1.1__1")
check_invalid("1.1e+_1")
check_invalid("1.1e+1__1")
check_invalid("123.dd")
check_invalid("123.5_dd")
check_invalid("dd.5")
check_invalid("7_dd")
check_invalid("1/dd")
check_invalid("1/123_dd")
check_invalid("789edd")
check_invalid("789e2_dd")
# Test catastrophic backtracking.
val = "9"*50 + "_"
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '" + val + "'",
F, val)
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1/" + val + "'",
F, "1/" + val)
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1." + val + "'",
F, "1." + val)
self.assertRaisesMessage(
ValueError, "Invalid literal for Fraction: '1.1+e" + val + "'",
F, "1.1+e" + val)
check_invalid(val)
check_invalid("1/" + val)
check_invalid("1." + val)
check_invalid("." + val)
check_invalid("1.1+e" + val)
check_invalid("1.1e" + val)
def test_limit_int(self):
maxdigits = 5000
with adjust_int_max_str_digits(maxdigits):
msg = 'Exceeds the limit'
val = '1' * maxdigits
num = (10**maxdigits - 1)//9
self.assertEqual((num, 1), _components(F(val)))
self.assertRaisesRegex(ValueError, msg, F, val + '1')
self.assertEqual((num, 2), _components(F(val + '/2')))
self.assertRaisesRegex(ValueError, msg, F, val + '1/2')
self.assertEqual((1, num), _components(F('1/' + val)))
self.assertRaisesRegex(ValueError, msg, F, '1/1' + val)
self.assertEqual(((10**(maxdigits+1) - 1)//9, 10**maxdigits),
_components(F('1.' + val)))
self.assertRaisesRegex(ValueError, msg, F, '1.1' + val)
self.assertEqual((num, 10**maxdigits), _components(F('.' + val)))
self.assertRaisesRegex(ValueError, msg, F, '.1' + val)
self.assertRaisesRegex(ValueError, msg, F, '1.1e1' + val)
self.assertEqual((11, 10), _components(F('1.1e' + '0' * maxdigits)))
self.assertRaisesRegex(ValueError, msg, F, '1.1e' + '0' * (maxdigits+1))
def testImmutable(self):
r = F(7, 3)
@@ -922,21 +910,21 @@ class FractionTest(unittest.TestCase):
self.assertTypedEquals(Root(4) ** F(2, 1), Root(4, F(1)))
self.assertTypedEquals(Root(4) ** F(-2, 1), Root(4, -F(1)))
self.assertTypedEquals(Root(4) ** F(-2, 3), Root(4, -3.0))
self.assertEqual(F(3, 2) ** SymbolicReal('X'), SymbolicReal('1.5 ** X'))
self.assertEqual(F(3, 2) ** SymbolicReal('X'), SymbolicReal('3/2 ** X'))
self.assertEqual(SymbolicReal('X') ** F(3, 2), SymbolicReal('X ** 1.5'))
self.assertTypedEquals(F(3, 2) ** Rect(2, 0), Polar(2.25, 0.0))
self.assertTypedEquals(F(1, 1) ** Rect(2, 3), Polar(1.0, 0.0))
self.assertTypedEquals(F(3, 2) ** Rect(2, 0), Polar(F(9,4), 0.0))
self.assertTypedEquals(F(1, 1) ** Rect(2, 3), Polar(F(1), 0.0))
self.assertTypedEquals(F(3, 2) ** RectComplex(2, 0), Polar(2.25, 0.0))
self.assertTypedEquals(F(1, 1) ** RectComplex(2, 3), Polar(1.0, 0.0))
self.assertTypedEquals(Polar(4, 2) ** F(3, 2), Polar(8.0, 3.0))
self.assertTypedEquals(Polar(4, 2) ** F(3, 1), Polar(64, 6))
self.assertTypedEquals(Polar(4, 2) ** F(-3, 1), Polar(0.015625, -6))
self.assertTypedEquals(Polar(4, 2) ** F(-3, 2), Polar(0.125, -3.0))
self.assertEqual(F(3, 2) ** SymbolicComplex('X'), SymbolicComplex('1.5 ** X'))
self.assertEqual(F(3, 2) ** SymbolicComplex('X'), SymbolicComplex('3/2 ** X'))
self.assertEqual(SymbolicComplex('X') ** F(3, 2), SymbolicComplex('X ** 1.5'))
self.assertEqual(F(3, 2) ** Symbolic('X'), Symbolic('1.5 ** X'))
self.assertEqual(F(3, 2) ** Symbolic('X'), Symbolic('3/2 ** X'))
self.assertEqual(Symbolic('X') ** F(3, 2), Symbolic('X ** 1.5'))
def testMixingWithDecimal(self):
@@ -1165,12 +1153,50 @@ class FractionTest(unittest.TestCase):
self.assertEqual(type(f.denominator), myint)
def test_format_no_presentation_type(self):
# Triples (fraction, specification, expected_result)
# Triples (fraction, specification, expected_result).
testcases = [
(F(1, 3), '', '1/3'),
(F(-1, 3), '', '-1/3'),
(F(3), '', '3'),
(F(-3), '', '-3'),
# Explicit sign handling
(F(2, 3), '+', '+2/3'),
(F(-2, 3), '+', '-2/3'),
(F(3), '+', '+3'),
(F(-3), '+', '-3'),
(F(2, 3), ' ', ' 2/3'),
(F(-2, 3), ' ', '-2/3'),
(F(3), ' ', ' 3'),
(F(-3), ' ', '-3'),
(F(2, 3), '-', '2/3'),
(F(-2, 3), '-', '-2/3'),
(F(3), '-', '3'),
(F(-3), '-', '-3'),
# Padding
(F(0), '5', ' 0'),
(F(2, 3), '5', ' 2/3'),
(F(-2, 3), '5', ' -2/3'),
(F(2, 3), '0', '2/3'),
(F(2, 3), '1', '2/3'),
(F(2, 3), '2', '2/3'),
# Alignment
(F(2, 3), '<5', '2/3 '),
(F(2, 3), '>5', ' 2/3'),
(F(2, 3), '^5', ' 2/3 '),
(F(2, 3), '=5', ' 2/3'),
(F(-2, 3), '<5', '-2/3 '),
(F(-2, 3), '>5', ' -2/3'),
(F(-2, 3), '^5', '-2/3 '),
(F(-2, 3), '=5', '- 2/3'),
# Fill
(F(2, 3), 'X>5', 'XX2/3'),
(F(-2, 3), '.<5', '-2/3.'),
(F(-2, 3), '\n^6', '\n-2/3\n'),
# Thousands separators
(F(1234, 5679), ',', '1,234/5,679'),
(F(-1234, 5679), '_', '-1_234/5_679'),
(F(1234567), '_', '1_234_567'),
(F(-1234567), ',', '-1,234,567'),
# Alternate form forces a slash in the output
(F(123), '#', '123/1'),
(F(-123), '#', '-123/1'),
(F(0), '#', '0/1'),
]
for fraction, spec, expected in testcases:
with self.subTest(fraction=fraction, spec=spec):
@@ -1385,11 +1411,8 @@ class FractionTest(unittest.TestCase):
(F('-1234.5678'), '08,.0f', '-001,235'),
(F('-1234.5678'), '09,.0f', '-0,001,235'),
# Corner-case - zero-padding specified through fill and align
# instead of the zero-pad character - in this case, treat '0' as a
# regular fill character and don't attempt to insert commas into
# the filled portion. This differs from the int and float
# behaviour.
(F('1234.5678'), '0=12,.2f', '00001,234.57'),
# instead of the zero-pad character.
(F('1234.5678'), '0=12,.2f', '0,001,234.57'),
# Corner case where it's not clear whether the '0' indicates zero
# padding or gives the minimum width, but there's still an obvious
# answer to give. We want this to work in case the minimum width
@@ -1534,13 +1557,17 @@ class FractionTest(unittest.TestCase):
'.%',
# Z instead of z for negative zero suppression
'Z.2f'
# z flag not supported for general formatting
'z',
# zero padding not supported for general formatting
'05',
]
for spec in invalid_specs:
with self.subTest(spec=spec):
with self.assertRaises(ValueError):
format(fraction, spec)
# @requires_IEEE_754
@requires_IEEE_754
def test_float_format_testfile(self):
with open(format_testfile, encoding="utf-8") as testfile:
for line in testfile:
@@ -1564,6 +1591,36 @@ class FractionTest(unittest.TestCase):
self.assertEqual(float(format(f, fmt2)), float(rhs))
self.assertEqual(float(format(-f, fmt2)), float('-' + rhs))
# TODO: RUSTPYTHON
# TypeError: '%' not supported between instances of 'Fraction' and 'complex'
@unittest.expectedFailure
def test_complex_handling(self):
# See issue gh-102840 for more details.
a = F(1, 2)
b = 1j
message = "unsupported operand type(s) for %s: '%s' and '%s'"
# test forward
self.assertRaisesMessage(TypeError,
message % ("%", "Fraction", "complex"),
operator.mod, a, b)
self.assertRaisesMessage(TypeError,
message % ("//", "Fraction", "complex"),
operator.floordiv, a, b)
self.assertRaisesMessage(TypeError,
message % ("divmod()", "Fraction", "complex"),
divmod, a, b)
# test reverse
self.assertRaisesMessage(TypeError,
message % ("%", "complex", "Fraction"),
operator.mod, b, a)
self.assertRaisesMessage(TypeError,
message % ("//", "complex", "Fraction"),
operator.floordiv, b, a)
self.assertRaisesMessage(TypeError,
message % ("divmod()", "complex", "Fraction"),
divmod, b, a)
if __name__ == '__main__':
unittest.main()
+54 -32
View File
@@ -18,6 +18,7 @@ except ImportError:
from unittest import TestCase, skipUnless
from test import support
from test.support import requires_subprocess
from test.support import threading_helper
from test.support import socket_helper
from test.support import warnings_helper
@@ -32,7 +33,7 @@ TIMEOUT = support.LOOPBACK_TIMEOUT
DEFAULT_ENCODING = 'utf-8'
# the dummy data returned by server over the data channel when
# RETR, LIST, NLST, MLSD commands are issued
RETR_DATA = 'abcde12345\r\n' * 1000 + 'non-ascii char \xAE\r\n'
RETR_DATA = 'abcde\xB9\xB2\xB3\xA4\xA6\r\n' * 1000
LIST_DATA = 'foo\r\nbar\r\n non-ascii char \xAE\r\n'
NLST_DATA = 'foo\r\nbar\r\n non-ascii char \xAE\r\n'
MLSD_DATA = ("type=cdir;perm=el;unique==keVO1+ZF4; test\r\n"
@@ -67,11 +68,11 @@ class DummyDTPHandler(asynchat.async_chat):
def __init__(self, conn, baseclass):
asynchat.async_chat.__init__(self, conn)
self.baseclass = baseclass
self.baseclass.last_received_data = ''
self.baseclass.last_received_data = bytearray()
self.encoding = baseclass.encoding
def handle_read(self):
new_data = self.recv(1024).decode(self.encoding, 'replace')
new_data = self.recv(1024)
self.baseclass.last_received_data += new_data
def handle_close(self):
@@ -80,7 +81,7 @@ class DummyDTPHandler(asynchat.async_chat):
# (behaviour witnessed with test_data_connection)
if not self.dtp_conn_closed:
self.baseclass.push('226 transfer complete')
self.close()
self.shutdown()
self.dtp_conn_closed = True
def push(self, what):
@@ -94,6 +95,9 @@ class DummyDTPHandler(asynchat.async_chat):
def handle_error(self):
default_error_handler()
def shutdown(self):
self.close()
class DummyFTPHandler(asynchat.async_chat):
@@ -107,7 +111,7 @@ class DummyFTPHandler(asynchat.async_chat):
self.in_buffer = []
self.dtp = None
self.last_received_cmd = None
self.last_received_data = ''
self.last_received_data = bytearray()
self.next_response = ''
self.next_data = None
self.rest = None
@@ -226,7 +230,7 @@ class DummyFTPHandler(asynchat.async_chat):
def cmd_quit(self, arg):
self.push('221 quit ok')
self.close()
self.shutdown()
def cmd_abor(self, arg):
self.push('226 abor ok')
@@ -313,7 +317,7 @@ class DummyFTPServer(asyncore.dispatcher, threading.Thread):
self.handler_instance = self.handler(conn, encoding=self.encoding)
def handle_connect(self):
self.close()
self.shutdown()
handle_read = handle_connect
def writable(self):
@@ -325,8 +329,8 @@ class DummyFTPServer(asyncore.dispatcher, threading.Thread):
if ssl is not None:
CERTFILE = os.path.join(os.path.dirname(__file__), "keycert3.pem")
CAFILE = os.path.join(os.path.dirname(__file__), "pycacert.pem")
CERTFILE = os.path.join(os.path.dirname(__file__), "certdata", "keycert3.pem")
CAFILE = os.path.join(os.path.dirname(__file__), "certdata", "pycacert.pem")
class SSLConnection(asyncore.dispatcher):
"""An asyncore.dispatcher subclass supporting TLS/SSL."""
@@ -425,12 +429,12 @@ if ssl is not None:
def handle_error(self):
default_error_handler()
def close(self):
def shutdown(self):
if (isinstance(self.socket, ssl.SSLSocket) and
self.socket._sslobj is not None):
self._do_ssl_shutdown()
else:
super(SSLConnection, self).close()
self.close()
class DummyTLS_DTPHandler(SSLConnection, DummyDTPHandler):
@@ -542,8 +546,8 @@ class TestFTPClass(TestCase):
self.assertFalse(self.client.passiveserver)
def test_voidcmd(self):
self.client.voidcmd('echo 200')
self.client.voidcmd('echo 299')
self.assertEqual(self.client.voidcmd('echo 200'), '200')
self.assertEqual(self.client.voidcmd('echo 299'), '299')
self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 199')
self.assertRaises(ftplib.error_reply, self.client.voidcmd, 'echo 300')
@@ -589,37 +593,44 @@ class TestFTPClass(TestCase):
def test_abort(self):
self.client.abort()
@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_retrbinary(self):
def callback(data):
received.append(data.decode(self.client.encoding))
received = []
self.client.retrbinary('retr', callback)
self.check_data(''.join(received), RETR_DATA)
self.client.retrbinary('retr', received.append)
self.check_data(b''.join(received),
RETR_DATA.encode(self.client.encoding))
@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_retrbinary_rest(self):
def callback(data):
received.append(data.decode(self.client.encoding))
for rest in (0, 10, 20):
received = []
self.client.retrbinary('retr', callback, rest=rest)
self.check_data(''.join(received), RETR_DATA[rest:])
self.client.retrbinary('retr', received.append, rest=rest)
self.check_data(b''.join(received),
RETR_DATA[rest:].encode(self.client.encoding))
@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_retrlines(self):
received = []
self.client.retrlines('retr', received.append)
self.check_data(''.join(received), RETR_DATA.replace('\r\n', ''))
@unittest.skip("TODO: RUSTPYTHON; weird limiting to 8192, something w/ buffering?")
@unittest.skip('TODO: RUSTPYTHON; weird limiting to 8192, something w/ buffering?')
def test_storbinary(self):
f = io.BytesIO(RETR_DATA.encode(self.client.encoding))
self.client.storbinary('stor', f)
self.check_data(self.server.handler_instance.last_received_data, RETR_DATA)
self.check_data(self.server.handler_instance.last_received_data,
RETR_DATA.encode(self.server.encoding))
# test new callback arg
flag = []
f.seek(0)
self.client.storbinary('stor', f, callback=lambda x: flag.append(None))
self.assertTrue(flag)
@unittest.skip('TODO: RUSTPYTHON')
# ssl_error.SSLWantReadError: The operation did not complete (read)
def test_storbinary_rest(self):
data = RETR_DATA.replace('\r\n', '\n').encode(self.client.encoding)
f = io.BytesIO(data)
@@ -628,11 +639,14 @@ class TestFTPClass(TestCase):
self.client.storbinary('stor', f, rest=r)
self.assertEqual(self.server.handler_instance.rest, str(r))
@unittest.skip('TODO: RUSTPYTHON')
# ssl_error.SSLWantReadError: The operation did not complete (read)
def test_storlines(self):
data = RETR_DATA.replace('\r\n', '\n').encode(self.client.encoding)
f = io.BytesIO(data)
self.client.storlines('stor', f)
self.check_data(self.server.handler_instance.last_received_data, RETR_DATA)
self.check_data(self.server.handler_instance.last_received_data,
RETR_DATA.encode(self.server.encoding))
# test new callback arg
flag = []
f.seek(0)
@@ -644,15 +658,21 @@ class TestFTPClass(TestCase):
with warnings_helper.check_warnings(('', BytesWarning), quiet=True):
self.assertRaises(TypeError, self.client.storlines, 'stor foo', f)
@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_nlst(self):
self.client.nlst()
self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1])
@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_dir(self):
l = []
self.client.dir(lambda x: l.append(x))
self.client.dir(l.append)
self.assertEqual(''.join(l), LIST_DATA.replace('\r\n', ''))
@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_mlsd(self):
list(self.client.mlsd())
list(self.client.mlsd(path='/'))
@@ -839,6 +859,8 @@ class TestFTPClass(TestCase):
f = io.BytesIO(b'x' * self.client.maxline * 2)
self.assertRaises(ftplib.Error, self.client.storlines, 'stor', f)
@unittest.skip('TODO: RUSTPYTHON')
# TimeoutError: The read operation timed out
def test_encoding_param(self):
encodings = ['latin-1', 'utf-8']
for encoding in encodings:
@@ -890,12 +912,10 @@ class TestIPv6Environment(TestCase):
def test_transfer(self):
def retr():
def callback(data):
received.append(data.decode(self.client.encoding))
received = []
self.client.retrbinary('retr', callback)
self.assertEqual(len(''.join(received)), len(RETR_DATA))
self.assertEqual(''.join(received), RETR_DATA)
self.client.retrbinary('retr', received.append)
self.assertEqual(b''.join(received),
RETR_DATA.encode(self.client.encoding))
self.client.set_pasv(True)
retr()
self.client.set_pasv(False)
@@ -903,7 +923,7 @@ class TestIPv6Environment(TestCase):
@skipUnless(ssl, "SSL not available")
@unittest.skip("TODO: RUSTPYTHON; figure out why do_handshake() is throwing 'ssl session has been shut down'. SslSession object?")
@requires_subprocess()
class TestTLS_FTPClassMixin(TestFTPClass):
"""Repeat TestFTPClass tests starting the TLS layer for both control
and data connections first.
@@ -920,7 +940,7 @@ class TestTLS_FTPClassMixin(TestFTPClass):
@skipUnless(ssl, "SSL not available")
@unittest.skip("TODO: RUSTPYTHON; fix ssl")
@requires_subprocess()
class TestTLS_FTPClass(TestCase):
"""Specific TLS_FTP class tests."""
@@ -1004,6 +1024,8 @@ class TestTLS_FTPClass(TestCase):
self.assertIs(sock.context, ctx)
self.assertIsInstance(sock, ssl.SSLSocket)
@unittest.skip('TODO: RUSTPYTHON')
# ssl_error.SSLWantReadError: The operation did not complete (read)
def test_ccc(self):
self.assertRaises(ValueError, self.client.ccc)
self.client.login(secure=True)