X Tutup
Skip to content

Commit 5ae75a6

Browse files
authored
Update the fractions and ftplib libraries + associated tests - v.3.13.10 (#6607)
* Updated ftplib + test library * Updated fractions + test library
1 parent 52dd829 commit 5ae75a6

File tree

4 files changed

+338
-197
lines changed

4 files changed

+338
-197
lines changed

Lib/fractions.py

Lines changed: 81 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,23 @@ def _round_to_figures(n, d, figures):
139139
return sign, significand, exponent
140140

141141

142+
# Pattern for matching non-float-style format specifications.
143+
_GENERAL_FORMAT_SPECIFICATION_MATCHER = re.compile(r"""
144+
(?:
145+
(?P<fill>.)?
146+
(?P<align>[<>=^])
147+
)?
148+
(?P<sign>[-+ ]?)
149+
# Alt flag forces a slash and denominator in the output, even for
150+
# integer-valued Fraction objects.
151+
(?P<alt>\#)?
152+
# We don't implement the zeropad flag since there's no single obvious way
153+
# to interpret it.
154+
(?P<minimumwidth>0|[1-9][0-9]*)?
155+
(?P<thousands_sep>[,_])?
156+
""", re.DOTALL | re.VERBOSE).fullmatch
157+
158+
142159
# Pattern for matching float-style format specifications;
143160
# supports 'e', 'E', 'f', 'F', 'g', 'G' and '%' presentation types.
144161
_FLOAT_FORMAT_SPECIFICATION_MATCHER = re.compile(r"""
@@ -414,27 +431,42 @@ def __str__(self):
414431
else:
415432
return '%s/%s' % (self._numerator, self._denominator)
416433

417-
def __format__(self, format_spec, /):
418-
"""Format this fraction according to the given format specification."""
419-
420-
# Backwards compatiblility with existing formatting.
421-
if not format_spec:
422-
return str(self)
434+
def _format_general(self, match):
435+
"""Helper method for __format__.
423436
437+
Handles fill, alignment, signs, and thousands separators in the
438+
case of no presentation type.
439+
"""
424440
# Validate and parse the format specifier.
425-
match = _FLOAT_FORMAT_SPECIFICATION_MATCHER(format_spec)
426-
if match is None:
427-
raise ValueError(
428-
f"Invalid format specifier {format_spec!r} "
429-
f"for object of type {type(self).__name__!r}"
430-
)
431-
elif match["align"] is not None and match["zeropad"] is not None:
432-
# Avoid the temptation to guess.
433-
raise ValueError(
434-
f"Invalid format specifier {format_spec!r} "
435-
f"for object of type {type(self).__name__!r}; "
436-
"can't use explicit alignment when zero-padding"
437-
)
441+
fill = match["fill"] or " "
442+
align = match["align"] or ">"
443+
pos_sign = "" if match["sign"] == "-" else match["sign"]
444+
alternate_form = bool(match["alt"])
445+
minimumwidth = int(match["minimumwidth"] or "0")
446+
thousands_sep = match["thousands_sep"] or ''
447+
448+
# Determine the body and sign representation.
449+
n, d = self._numerator, self._denominator
450+
if d > 1 or alternate_form:
451+
body = f"{abs(n):{thousands_sep}}/{d:{thousands_sep}}"
452+
else:
453+
body = f"{abs(n):{thousands_sep}}"
454+
sign = '-' if n < 0 else pos_sign
455+
456+
# Pad with fill character if necessary and return.
457+
padding = fill * (minimumwidth - len(sign) - len(body))
458+
if align == ">":
459+
return padding + sign + body
460+
elif align == "<":
461+
return sign + body + padding
462+
elif align == "^":
463+
half = len(padding) // 2
464+
return padding[:half] + sign + body + padding[half:]
465+
else: # align == "="
466+
return sign + padding + body
467+
468+
def _format_float_style(self, match):
469+
"""Helper method for __format__; handles float presentation types."""
438470
fill = match["fill"] or " "
439471
align = match["align"] or ">"
440472
pos_sign = "" if match["sign"] == "-" else match["sign"]
@@ -449,6 +481,9 @@ def __format__(self, format_spec, /):
449481
trim_point = not alternate_form
450482
exponent_indicator = "E" if presentation_type in "EFG" else "e"
451483

484+
if align == '=' and fill == '0':
485+
zeropad = True
486+
452487
# Round to get the digits we need, figure out where to place the point,
453488
# and decide whether to use scientific notation. 'point_pos' is the
454489
# relative to the _end_ of the digit string: that is, it's the number
@@ -530,7 +565,25 @@ def __format__(self, format_spec, /):
530565
else: # align == "="
531566
return sign + padding + body
532567

533-
def _operator_fallbacks(monomorphic_operator, fallback_operator):
568+
def __format__(self, format_spec, /):
569+
"""Format this fraction according to the given format specification."""
570+
571+
if match := _GENERAL_FORMAT_SPECIFICATION_MATCHER(format_spec):
572+
return self._format_general(match)
573+
574+
if match := _FLOAT_FORMAT_SPECIFICATION_MATCHER(format_spec):
575+
# Refuse the temptation to guess if both alignment _and_
576+
# zero padding are specified.
577+
if match["align"] is None or match["zeropad"] is None:
578+
return self._format_float_style(match)
579+
580+
raise ValueError(
581+
f"Invalid format specifier {format_spec!r} "
582+
f"for object of type {type(self).__name__!r}"
583+
)
584+
585+
def _operator_fallbacks(monomorphic_operator, fallback_operator,
586+
handle_complex=True):
534587
"""Generates forward and reverse operators given a purely-rational
535588
operator and a function from the operator module.
536589
@@ -617,7 +670,7 @@ def forward(a, b):
617670
return monomorphic_operator(a, Fraction(b))
618671
elif isinstance(b, float):
619672
return fallback_operator(float(a), b)
620-
elif isinstance(b, complex):
673+
elif handle_complex and isinstance(b, complex):
621674
return fallback_operator(complex(a), b)
622675
else:
623676
return NotImplemented
@@ -630,7 +683,7 @@ def reverse(b, a):
630683
return monomorphic_operator(Fraction(a), b)
631684
elif isinstance(a, numbers.Real):
632685
return fallback_operator(float(a), float(b))
633-
elif isinstance(a, numbers.Complex):
686+
elif handle_complex and isinstance(a, numbers.Complex):
634687
return fallback_operator(complex(a), complex(b))
635688
else:
636689
return NotImplemented
@@ -781,22 +834,22 @@ def _floordiv(a, b):
781834
"""a // b"""
782835
return (a.numerator * b.denominator) // (a.denominator * b.numerator)
783836

784-
__floordiv__, __rfloordiv__ = _operator_fallbacks(_floordiv, operator.floordiv)
837+
__floordiv__, __rfloordiv__ = _operator_fallbacks(_floordiv, operator.floordiv, False)
785838

786839
def _divmod(a, b):
787840
"""(a // b, a % b)"""
788841
da, db = a.denominator, b.denominator
789842
div, n_mod = divmod(a.numerator * db, da * b.numerator)
790843
return div, Fraction(n_mod, da * db)
791844

792-
__divmod__, __rdivmod__ = _operator_fallbacks(_divmod, divmod)
845+
__divmod__, __rdivmod__ = _operator_fallbacks(_divmod, divmod, False)
793846

794847
def _mod(a, b):
795848
"""a % b"""
796849
da, db = a.denominator, b.denominator
797850
return Fraction((a.numerator * db) % (b.numerator * da), da * db)
798851

799-
__mod__, __rmod__ = _operator_fallbacks(_mod, operator.mod)
852+
__mod__, __rmod__ = _operator_fallbacks(_mod, operator.mod, False)
800853

801854
def __pow__(a, b):
802855
"""a ** b
@@ -825,8 +878,10 @@ def __pow__(a, b):
825878
# A fractional power will generally produce an
826879
# irrational number.
827880
return float(a) ** float(b)
828-
else:
881+
elif isinstance(b, (float, complex)):
829882
return float(a) ** b
883+
else:
884+
return NotImplemented
830885

831886
def __rpow__(b, a):
832887
"""a ** b"""

Lib/ftplib.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -900,11 +900,17 @@ def ftpcp(source, sourcename, target, targetname = '', type = 'I'):
900900

901901
def test():
902902
'''Test program.
903-
Usage: ftp [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
903+
Usage: ftplib [-d] [-r[file]] host [-l[dir]] [-d[dir]] [-p] [file] ...
904904
905-
-d dir
906-
-l list
907-
-p password
905+
Options:
906+
-d increase debugging level
907+
-r[file] set alternate ~/.netrc file
908+
909+
Commands:
910+
-l[dir] list directory
911+
-d[dir] change the current directory
912+
-p toggle passive and active mode
913+
file retrieve the file and write it to stdout
908914
'''
909915

910916
if len(sys.argv) < 2:
@@ -930,15 +936,14 @@ def test():
930936
netrcobj = netrc.netrc(rcfile)
931937
except OSError:
932938
if rcfile is not None:
933-
sys.stderr.write("Could not open account file"
934-
" -- using anonymous login.")
939+
print("Could not open account file -- using anonymous login.",
940+
file=sys.stderr)
935941
else:
936942
try:
937943
userid, acct, passwd = netrcobj.authenticators(host)
938-
except KeyError:
944+
except (KeyError, TypeError):
939945
# no account for host
940-
sys.stderr.write(
941-
"No account -- using anonymous login.")
946+
print("No account -- using anonymous login.", file=sys.stderr)
942947
ftp.login(userid, passwd, acct)
943948
for file in sys.argv[2:]:
944949
if file[:2] == '-l':
@@ -951,7 +956,9 @@ def test():
951956
ftp.set_pasv(not ftp.passiveserver)
952957
else:
953958
ftp.retrbinary('RETR ' + file, \
954-
sys.stdout.write, 1024)
959+
sys.stdout.buffer.write, 1024)
960+
sys.stdout.buffer.flush()
961+
sys.stdout.flush()
955962
ftp.quit()
956963

957964

0 commit comments

Comments
 (0)
X Tutup