Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b0ec8ee
Updated bdb.py + test_bdb.py
terryluan12 Dec 30, 2025
91d4447
Deleted _pycodecs.py
terryluan12 Dec 30, 2025
ac9bd54
Updated code.py library
terryluan12 Dec 30, 2025
d5f6198
Updated the _pydatetime.py lib
terryluan12 Dec 30, 2025
f585018
Removed distutils package
terryluan12 Dec 30, 2025
da4a841
Updated doctest package
terryluan12 Dec 30, 2025
d7405c9
* Updated datetimetester.py
terryluan12 Dec 30, 2025
e82917e
Updated enum and test_enum.py
terryluan12 Dec 30, 2025
6e59ca5
Updated filecmp + test_filecmp
terryluan12 Dec 30, 2025
aae2dcf
Updated fractions + test_fractions
terryluan12 Dec 30, 2025
fd34286
Updated ftplib + test_ftplib
terryluan12 Dec 30, 2025
77db70c
Updated hmac + test_hmac
terryluan12 Dec 30, 2025
7c13c61
* Updated mailbox + added test_mailbox.py
terryluan12 Dec 30, 2025
3752174
Updated nturl2path.py
terryluan12 Dec 30, 2025
cd5865e
Added pathlib + test_pathlib packages
terryluan12 Dec 30, 2025
0e3365a
Updated pkgutil.py & test_pkgutil.py
terryluan12 Dec 30, 2025
d313adc
Updated platform.py + test_platform.py
terryluan12 Dec 30, 2025
6da715b
Updated plistlib + test_plistlib
terryluan12 Dec 30, 2025
2ee4e37
Merge branch 'add_tests' into update_simple_packages_2
terryluan12 Dec 30, 2025
28493a7
Updated enum and plistlib tests using the script
terryluan12 Dec 30, 2025
312f7d9
Updated pkgutil test with the script
terryluan12 Dec 30, 2025
39f990e
Ran/updated ftplib +test_hmac + pathlib tests with the script
terryluan12 Dec 30, 2025
1cd4ad6
Added comment to pathlib
terryluan12 Dec 30, 2025
88857d9
Clarified the comments at the top of test_pathlib
terryluan12 Dec 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Updated fractions + test_fractions
  • Loading branch information
terryluan12 committed Dec 30, 2025
commit aae2dcfde4bc162c9f9d0007c0e7a918deb9163c
107 changes: 81 additions & 26 deletions Lib/fractions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"""
Expand Down Expand Up @@ -414,27 +431,42 @@ def __str__(self):
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"]
Expand All @@ -449,6 +481,9 @@ def __format__(self, format_spec, /):
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
Expand Down Expand Up @@ -530,7 +565,25 @@ def __format__(self, format_spec, /):
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.

Expand Down Expand Up @@ -617,7 +670,7 @@ def forward(a, b):
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
Expand All @@ -630,7 +683,7 @@ def reverse(b, a):
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
Expand Down Expand Up @@ -781,22 +834,22 @@ def _floordiv(a, b):
"""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)"""
da, db = a.denominator, b.denominator
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
Expand Down Expand Up @@ -825,8 +878,10 @@ def __pow__(a, b):
# 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"""
Expand Down
Loading