Sync stdlib utilities from CPython 3.15.0 final (#9111)

Regenerate nine utility modules and associated tests with scripts/update_lib
from CPython d220409a13b77de7a6c30abe16ea90db01528edc.
Preserve existing mimetypes fixes by excluding that module from this split.

OpenAI Codex assisted with source audit, update_lib execution, and focused
release validation: 64 tests passed, one existing WASI-only skip.

Assisted-by: OpenAI Codex:model version unavailable
This commit is contained in:
youknowdot authored and GitHub committed 2026-10-14 21:45:57 +09:00
1 parent 0816af6543
commit a85a6cd762
15 files changed
+341 -95

No files matched your search

+4 -2
View File
@@ -618,7 +618,8 @@ class RawConfigParser(MutableMapping):
_OPT_TMPL = r"""
(?P<option> # very permissive!
(?:(?!{delim})\S)* # non-delimiter non-whitespace
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
(?:(?:(?!{delim})\s)+ # optionally more
(?:(?!{delim})\S)+)*) # space-separated words
\s*(?P<vi>{delim})\s* # any number of space/tab,
# followed by any of the
# allowed delimiters,
@@ -628,7 +629,8 @@ class RawConfigParser(MutableMapping):
_OPT_NV_TMPL = r"""
(?P<option> # very permissive!
(?:(?!{delim})\S)* # non-delimiter non-whitespace
(?:\s+(?:(?!{delim})\S)+)*) # optionally more words
(?:(?:(?!{delim})\s)+ # optionally more
(?:(?!{delim})\S)+)*) # space-separated words
\s*(?: # any number of space/tab,
(?P<vi>{delim})\s* # optionally followed by
# any of the allowed
Vendored
+23 -14
View File
@@ -81,8 +81,6 @@ __all__ = ["QUOTE_MINIMAL", "QUOTE_ALL", "QUOTE_NONNUMERIC", "QUOTE_NONE",
"unregister_dialect", "DictReader", "DictWriter",
"unix_dialect"]
__version__ = "1.0"
class Dialect:
"""Describe a CSV dialect.
@@ -378,31 +376,33 @@ class Sniffer:
try and evaluate the smallest portion of the data possible, evaluating
additional chunks as necessary.
"""
from collections import Counter, defaultdict
data = list(filter(None, data.split('\n')))
ascii = [chr(c) for c in range(127)] # 7-bit ASCII
# build frequency tables
chunkLength = min(10, len(data))
iteration = 0
charFrequency = {}
num_lines = 0
# {char -> {count_per_line -> num_lines_with_that_count}}
char_frequency = defaultdict(Counter)
modes = {}
delims = {}
start, end = 0, chunkLength
while start < len(data):
iteration += 1
for line in data[start:end]:
for char in ascii:
metaFrequency = charFrequency.get(char, {})
# must count even if frequency is 0
freq = line.count(char)
# value is the mode
metaFrequency[freq] = metaFrequency.get(freq, 0) + 1
charFrequency[char] = metaFrequency
num_lines += 1
for char, count in Counter(line).items():
if char.isascii():
char_frequency[char][count] += 1
for char in charFrequency.keys():
items = list(charFrequency[char].items())
for char, counts in char_frequency.items():
items = list(counts.items())
missed_lines = num_lines - sum(counts.values())
if missed_lines:
# Store the number of lines 'char' was missing from.
items.append((0, missed_lines))
if len(items) == 1 and items[0][0] == 0:
continue
# get the mode of the frequencies
@@ -525,3 +525,12 @@ class Sniffer:
hasHeader -= 1
return hasHeader > 0
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "1.0" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+5 -5
View File
@@ -242,11 +242,6 @@ class Fraction(numbers.Rational):
self._denominator = 1
return self
elif isinstance(numerator, numbers.Rational):
self._numerator = numerator.numerator
self._denominator = numerator.denominator
return self
elif (isinstance(numerator, float) or
(not isinstance(numerator, type) and
hasattr(numerator, 'as_integer_ratio'))):
@@ -282,6 +277,11 @@ class Fraction(numbers.Rational):
if m.group('sign') == '-':
numerator = -numerator
elif isinstance(numerator, numbers.Rational):
self._numerator = numerator.numerator
self._denominator = numerator.denominator
return self
else:
raise TypeError("argument should be a string or a Rational "
"instance or have the as_integer_ratio() method")
+58 -3
View File
@@ -199,6 +199,7 @@ class TopologicalSorter:
self._ready_nodes.append(successor)
self._nfinished += 1
# See note "On Finding Cycles" at the bottom.
def _find_cycle(self):
n2i = self._node2info
stack = []
@@ -212,8 +213,6 @@ class TopologicalSorter:
while True:
if node in seen:
# If we have seen already the node and is in the
# current stack we have found a cycle.
if node in node2stacki:
return stack[node2stacki[node] :] + [node]
# else go on to get next successor
@@ -228,11 +227,15 @@ class TopologicalSorter:
while stack:
try:
node = itstack[-1]()
break
break # resume at top of "while True:"
except StopIteration:
# no more successors; pop the stack
# and continue looking up
del node2stacki[stack.pop()]
itstack.pop()
else:
# stack is empty; look for a fresh node to
# start over from (a node not yet in seen)
break
return None
@@ -252,3 +255,55 @@ class TopologicalSorter:
self.done(*node_group)
__class_getitem__ = classmethod(GenericAlias)
# On Finding Cycles
# -----------------
# There is a (at least one) total order if and only if the graph is
# acyclic.
#
# When it is cyclic, "there's a cycle - somewhere!" isn't very helpful.
# In theory, it would be most helpful to partition the graph into
# strongly connected components (SCCs) and display those with more than
# one node. Then all cycles could easily be identified "by eyeball".
#
# That's a lot of work, though, and we can get most of the benefit much
# more easily just by showing a single specific cycle.
#
# Approaches to that are based on breadth first or depth first search
# (BFS or DFS). BFS is most natural, which can easily be arranged to
# find a shortest-possible cycle. But memory burden can be high, because
# every path-in-progress has to keep its own idea of what "the path" is
# so far.
#
# DFS is much easier on RAM, only requiring keeping track of _the_ path
# from the starting node to the current node at the current recursion
# level. But there may be any number of nodes, and so there's no bound
# on recursion depth short of the total number of nodes.
#
# So we use an iterative version of DFS, keeping an exploit list
# (`stack`) of the path so far. A parallel stack (`itstack`) holds the
# `__next__` method of an iterator over the current level's node's
# successors, so when backtracking to a shallower level we can just call
# that to get the node's next successor. This is state that a recursive
# version would implicitly store in a `for` loop's internals.
#
# `seen()` is a set recording which nodes have already been, at some
# time, pushed on the stack. If a node has been pushed on the stack, DFS
# will find any cycle it's part of, so there's no need to ever look at
# it again.
#
# Finally, `node2stacki` maps a node to its index on the current stack,
# for and only for nodes currently _on_ the stack. If a successor to be
# pushed on the stack is in that dict, the node is already on the path,
# at that index. The cycle is then `stack[that_index :] + [node]`.
#
# As is often the case when removing recursion, the control flow looks a
# bit off. The "while True:" loop here rarely actually loops - it's only
# looking to go "up the stack" until finding a level that has another
# successor to consider, emulating a chain of returns in a recursive
# version.
#
# Worst cases: O(V+E) for time, and O(V) for memory, where V is the
# number of nodes and E the number of edges (which may be quadratic in
# V!). It requires care to ensure these bounds are met.
+6 -6
View File
@@ -494,7 +494,7 @@ def nsmallest(n, iterable, key=None):
pass
else:
if n >= size:
return sorted(iterable, key=key)[:n]
return sorted(iterable, key=key)
# When key is none, use simpler decoration
if key is None:
@@ -511,7 +511,7 @@ def nsmallest(n, iterable, key=None):
for elem in it:
if elem < top:
_heapreplace(result, (elem, order))
top, _order = result[0]
top = result[0][0]
order += 1
result.sort()
return [elem for (elem, order) in result]
@@ -529,7 +529,7 @@ def nsmallest(n, iterable, key=None):
k = key(elem)
if k < top:
_heapreplace(result, (k, order, elem))
top, _order, _elem = result[0]
top = result[0][0]
order += 1
result.sort()
return [elem for (k, order, elem) in result]
@@ -554,7 +554,7 @@ def nlargest(n, iterable, key=None):
pass
else:
if n >= size:
return sorted(iterable, key=key, reverse=True)[:n]
return sorted(iterable, key=key, reverse=True)
# When key is none, use simpler decoration
if key is None:
@@ -569,7 +569,7 @@ def nlargest(n, iterable, key=None):
for elem in it:
if top < elem:
_heapreplace(result, (elem, order))
top, _order = result[0]
top = result[0][0]
order -= 1
result.sort(reverse=True)
return [elem for (elem, order) in result]
@@ -587,7 +587,7 @@ def nlargest(n, iterable, key=None):
k = key(elem)
if top < k:
_heapreplace(result, (k, order, elem))
top, _order, _elem = result[0]
top = result[0][0]
order -= 1
result.sort(reverse=True)
return [elem for (k, order, elem) in result]
+13 -3
View File
@@ -8,9 +8,6 @@ and networks.
"""
__version__ = '1.0'
import functools
IPV4LENGTH = 32
@@ -1479,6 +1476,10 @@ class IPv4Interface(IPv4Address):
return '%s/%s' % (self._string_from_ip_int(self._ip),
self.hostmask)
@property
def is_unspecified(self):
return self._ip == 0 and self.network.is_unspecified
class IPv4Network(_BaseV4, _BaseNetwork):
@@ -2415,3 +2416,12 @@ class _IPv6Constants:
IPv6Address._constants = _IPv6Constants
IPv6Network._constants = _IPv6Constants
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "1.0" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+31 -25
View File
@@ -48,11 +48,12 @@ class _netrclex:
def get_token(self):
if self.pushback:
return self.pushback.pop(0)
token = ""
token = None
fiter = iter(self._read_char, "")
for ch in fiter:
if ch in self.whitespace:
continue
token = ""
if ch == '"':
for ch in fiter:
if ch == '"':
@@ -95,10 +96,10 @@ class netrc:
while 1:
# Look for a machine, default, or macdef top-level keyword
saved_lineno = lexer.lineno
toplevel = tt = lexer.get_token()
if not tt:
tt = lexer.get_token()
if tt is None:
break
elif tt[0] == '#':
elif tt.startswith('#'):
if lexer.lineno == saved_lineno and len(tt) == 1:
lexer.instream.readline()
continue
@@ -135,40 +136,45 @@ class netrc:
while 1:
prev_lineno = lexer.lineno
tt = lexer.get_token()
if tt.startswith('#'):
if tt is not None and tt.startswith('#'):
if lexer.lineno == prev_lineno:
lexer.instream.readline()
continue
if tt in {'', 'machine', 'default', 'macdef'}:
if tt in {None, 'machine', 'default', 'macdef'}:
self.hosts[entryname] = (login, account, password)
lexer.push_token(tt)
break
elif tt == 'login' or tt == 'user':
login = lexer.get_token()
login = lexer.get_token() or ''
elif tt == 'account':
account = lexer.get_token()
account = lexer.get_token() or ''
elif tt == 'password':
password = lexer.get_token()
password = lexer.get_token() or ''
else:
raise NetrcParseError("bad follower token %r" % tt,
file, lexer.lineno)
self._security_check(fp, default_netrc, self.hosts[entryname][0])
def _security_check(self, fp, default_netrc, login):
if _can_security_check() and default_netrc and login != "anonymous":
prop = os.fstat(fp.fileno())
current_user_id = os.getuid()
if prop.st_uid != current_user_id:
fowner = _getpwuid(prop.st_uid)
user = _getpwuid(current_user_id)
raise NetrcParseError(
(f"~/.netrc file owner ({fowner}, {user}) does not match"
" current user"))
if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)):
raise NetrcParseError(
"~/.netrc access too permissive: access"
" permissions must restrict access to only"
" the owner")
if _can_security_check() and default_netrc:
for entry in self.hosts.values():
if entry[0] != "anonymous":
# Raises on security issue; once passed once can exit.
self._security_check(fp)
return
def _security_check(self, fp):
prop = os.fstat(fp.fileno())
current_user_id = os.getuid()
if prop.st_uid != current_user_id:
fowner = _getpwuid(prop.st_uid)
user = _getpwuid(current_user_id)
raise NetrcParseError(
f"~/.netrc file owner ({fowner}) does not match"
f" current user ({user})")
if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)):
raise NetrcParseError(
"~/.netrc access too permissive: access"
" permissions must restrict access to only"
" the owner")
def authenticators(self, host):
"""Return a (user, account, password) tuple for given host."""
+14 -21
View File
@@ -2,7 +2,7 @@ r"""plistlib.py -- a tool to generate and parse MacOSX .plist files.
The property list (.plist) file format is a simple XML pickle supporting
basic object types, like dictionaries, lists, numbers and strings.
Usually the top level object is a dictionary.
Usually the top level object is a dictionary or a frozen dictionary.
To write out a plist file, use the dump(value, file)
function. 'value' is the top level object, 'file' is
@@ -122,13 +122,7 @@ _controlCharPat = re.compile(
r"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f]")
def _encode_base64(s, maxlinelength=76):
# copied from base64.encodebytes(), with added maxlinelength argument
maxbinsize = (maxlinelength//4)*3
pieces = []
for i in range(0, len(s), maxbinsize):
chunk = s[i : i + maxbinsize]
pieces.append(binascii.b2a_base64(chunk))
return b''.join(pieces)
return binascii.b2a_base64(s, wrapcol=maxlinelength, newline=False)
def _decode_base64(s):
if isinstance(s, str):
@@ -363,7 +357,7 @@ class _PlistWriter(_DumbXMLWriter):
elif isinstance(value, float):
self.simple_element("real", repr(value))
elif isinstance(value, dict):
elif isinstance(value, (dict, frozendict)):
self.write_dict(value)
elif isinstance(value, (bytes, bytearray)):
@@ -382,11 +376,10 @@ class _PlistWriter(_DumbXMLWriter):
def write_bytes(self, data):
self.begin_element("data")
self._indent_level -= 1
maxlinelength = max(
16,
76 - len((self.indent * self._indent_level).expandtabs()))
for line in _encode_base64(data, maxlinelength).split(b"\n"):
wrapcol = 76 - len((self.indent * self._indent_level).expandtabs())
wrapcol = max(16, wrapcol)
encoded = binascii.b2a_base64(data, wrapcol=wrapcol, newline=False)
for line in encoded.split(b"\n"):
if line:
self.writeln(line)
self._indent_level += 1
@@ -460,7 +453,7 @@ class InvalidFileException (ValueError):
def __init__(self, message="Invalid file"):
ValueError.__init__(self, message)
_BINARY_FORMAT = {1: 'B', 2: 'H', 4: 'L', 8: 'Q'}
_BINARY_FORMAT = frozendict({1: 'B', 2: 'H', 4: 'L', 8: 'Q'})
_undefined = object()
@@ -722,7 +715,7 @@ class _BinaryPlistWriter (object):
self._objidtable[id(value)] = refnum
# And finally recurse into containers
if isinstance(value, dict):
if isinstance(value, (dict, frozendict)):
keys = []
values = []
items = value.items()
@@ -843,7 +836,7 @@ class _BinaryPlistWriter (object):
self._write_size(0xA0, s)
self._fp.write(struct.pack('>' + self._ref_format * s, *refs))
elif isinstance(value, dict):
elif isinstance(value, (dict, frozendict)):
keyRefs, valRefs = [], []
if self._sort_keys:
@@ -876,18 +869,18 @@ def _is_fmt_binary(header):
# Generic bits
#
_FORMATS={
FMT_XML: dict(
_FORMATS=frozendict({
FMT_XML: frozendict(
detect=_is_fmt_xml,
parser=_PlistParser,
writer=_PlistWriter,
),
FMT_BINARY: dict(
FMT_BINARY: frozendict(
detect=_is_fmt_binary,
parser=_BinaryPlistParser,
writer=_BinaryPlistWriter,
)
}
})
def load(fp, *, fmt=None, dict_type=dict, aware_datetime=False):
+10 -9
View File
@@ -136,7 +136,7 @@ import sys
from fractions import Fraction
from decimal import Decimal
from itertools import count, groupby, repeat
from itertools import compress, count, groupby, repeat
from bisect import bisect_left, bisect_right
from math import hypot, sqrt, fabs, exp, erfc, tau, log, fsum, sumprod
from math import isfinite, isinf, pi, cos, sin, tan, cosh, asin, atan, acos
@@ -145,6 +145,7 @@ from operator import itemgetter
from collections import Counter, namedtuple, defaultdict
_SQRT2 = sqrt(2.0)
_SQRT2PI = sqrt(tau)
_random = random
## Exceptions ##############################################################
@@ -194,9 +195,9 @@ def fmean(data, weights=None):
n = len(data)
except TypeError:
# Handle iterators that do not define __len__().
counter = count()
total = fsum(map(itemgetter(0), zip(data, counter)))
n = next(counter)
counter = count(1)
total = fsum(compress(data, counter))
n = next(counter) - 1
else:
total = fsum(data)
@@ -247,7 +248,7 @@ def geometric_mean(data):
elif x == 0.0:
found_zero = True
else:
raise StatisticsError('No negative inputs allowed', x)
raise StatisticsError(f'No negative inputs allowed: {x!r}')
total = fsum(map(log, count_positive(data)))
@@ -1257,11 +1258,11 @@ class NormalDist:
def pdf(self, x):
"Probability density function. P(x <= X < x+dx) / dx"
variance = self._sigma * self._sigma
if not variance:
sigma = self._sigma
if not sigma:
raise StatisticsError('pdf() not defined when sigma is zero')
diff = x - self._mu
return exp(diff * diff / (-2.0 * variance)) / sqrt(tau * variance)
z = (x - self._mu) / sigma
return exp(-0.5 * z * z) / (_SQRT2PI * sigma)
def cdf(self, x):
"Cumulative distribution function. P(X <= x)"
+30 -3
View File
@@ -43,7 +43,7 @@ class CfgParserTestCaseClass:
default_section = configparser.DEFAULTSECT
interpolation = configparser._UNSET
def newconfig(self, defaults=None):
def newconfig(self, defaults=None, **kwargs):
arguments = dict(
defaults=defaults,
allow_no_value=self.allow_no_value,
@@ -56,6 +56,7 @@ class CfgParserTestCaseClass:
default_section=self.default_section,
interpolation=self.interpolation,
)
arguments.update(kwargs)
instance = self.config_class(**arguments)
return instance
@@ -358,6 +359,32 @@ boolean {0[0]} NO
the larch {0[1]} 1
""".format(self.delimiters)))
@support.subTests('data', [
'foo bar=baz',
'foo bar=baz',
'foo=bar=baz',
'foo = bar=baz',
'foo\t \t=\t \tbar=baz',
])
def test_space_delimiter(self, data):
# gh-156353: Space should be accepted as a delimiter
cf = self.newconfig(delimiters=(' ', '='))
cf.read_string(f"[all]\n{data}")
self.assertEqual(cf.options('all'), ['foo'])
self.assertEqual(cf.get('all', 'foo'), 'bar=baz')
@support.subTests('delimiter', ' =:;#x\t\0\N{RS}\N{CEDILLA}\N{CAT}')
@support.subTests('space_before', ['', ' ', '\t', ' \t'])
@support.subTests('space_after', ['', ' ', '\t', ' \t'])
def test_any_delimiter(self, delimiter, space_before, space_after):
cf = self.newconfig(
delimiters=(delimiter,),
inline_comment_prefixes=None,
)
cf.read_string(f"[all]\nfoo{space_before}{delimiter}{space_after}bar=baz")
self.assertEqual(cf.options('all'), ['foo'])
self.assertEqual(cf.get('all', 'foo'), 'bar=baz')
def test_basic_from_dict(self):
config = {
"Foo Bar": {
@@ -1991,8 +2018,8 @@ class ConvertersTestCase(BasicTestCase, unittest.TestCase):
config_class = configparser.ConfigParser
def newconfig(self, defaults=None):
instance = super().newconfig(defaults=defaults)
def newconfig(self, defaults=None, **kwargs):
instance = super().newconfig(defaults=defaults, **kwargs)
instance.converters['list'] = lambda v: [e.strip() for e in v.split()
if e.strip()]
return instance
+74
View File
@@ -1308,6 +1308,19 @@ class TestDialectValidity(unittest.TestCase):
self.assertRaises(ValueError, create_invalid, field_name, " ",
skipinitialspace=True)
def test_dialect_getattr_non_attribute_error_propagates(self):
# gh-145966: non-AttributeError exceptions raised by __getattr__
# during dialect attribute lookup must propagate, not be silenced.
class BadDialect:
def __getattr__(self, name):
raise RuntimeError("boom")
with self.assertRaises(RuntimeError):
csv.reader([], dialect=BadDialect())
with self.assertRaises(RuntimeError):
csv.writer(StringIO(), dialect=BadDialect())
class TestSniffer(unittest.TestCase):
sample1 = """\
@@ -1502,6 +1515,56 @@ ghi\0jkl
dialect = sniffer.sniff(self.sample9)
self.assertTrue(dialect.doublequote)
def test_guess_delimiter_crlf_not_chosen(self):
# Ensure that we pick the real delimiter ("|") over "\r" in a tie.
sniffer = csv.Sniffer()
sample = "a|b\r\nc|d\r\ne|f\r\n"
self.assertEqual(sniffer.sniff(sample).delimiter, "|")
self.assertNotEqual(sniffer.sniff(sample).delimiter, "\r")
def test_zero_mode_tie_order_independence(self):
sniffer = csv.Sniffer()
# ":" appears in half the rows (1, 0, 1, 0) - a tie between
# 0 and 1 per line.
# "," appears once every row (true delimiter).
#
# Even if the zero-frequency bucket is appended vs. inserted, the tie
# yields an adjusted score of 0, so ":" should not be promoted and
# "," must be selected.
sample = (
"a,b:c\n"
"d,e\n"
"f,g:c\n"
"h,i\n"
)
dialect = sniffer.sniff(sample)
self.assertEqual(dialect.delimiter, ",")
def test_zero_mode_tie_order_comma_first(self):
sniffer = csv.Sniffer()
pattern = (
"a,b\n"
"c:d\n"
"e,f\n"
"g:h\n"
)
sample = pattern * 10
with self.assertRaisesRegex(csv.Error, "Could not determine delimiter"):
sniffer.sniff(sample)
def test_zero_mode_tie_order_colon_first(self):
sniffer = csv.Sniffer()
pattern = (
"a:b\n"
"c,d\n"
"e:f\n"
"g,h\n"
)
sample = pattern * 10
with self.assertRaisesRegex(csv.Error, "Could not determine delimiter"):
sniffer.sniff(sample)
def test_sniff_regex_backtracking(self):
# gh-109638: this artificial sample used to take minutes.
sniffer = csv.Sniffer()
@@ -1719,5 +1782,16 @@ class MiscTestCase(unittest.TestCase):
with self.subTest(tp=tp):
check_disallow_instantiation(self, tp)
class TestModule(unittest.TestCase):
def test_deprecated__version__(self):
with self.assertWarnsRegex(
DeprecationWarning,
"'__version__' is deprecated and slated for removal in Python 3.20",
) as cm:
getattr(csv, "__version__")
self.assertEqual(cm.filename, __file__)
if __name__ == '__main__':
unittest.main()
+14
View File
@@ -2303,6 +2303,10 @@ class IpaddrUnitTest(unittest.TestCase):
self.assertEqual(False, ipaddress.ip_network('240.0.0.0').is_multicast)
self.assertEqual(True, ipaddress.ip_network('240.0.0.0').is_reserved)
self.assertTrue(ipaddress.ip_interface('0.0.0.0/32').is_unspecified)
self.assertFalse(ipaddress.ip_interface('0.0.0.0/31').is_unspecified)
self.assertFalse(ipaddress.ip_interface('1.2.3.4/32').is_unspecified)
self.assertEqual(True, ipaddress.ip_interface(
'192.168.1.1/17').is_private)
self.assertEqual(False, ipaddress.ip_network('192.169.0.0').is_private)
@@ -2851,5 +2855,15 @@ class IpaddrUnitTest(unittest.TestCase):
)
class TestModule(unittest.TestCase):
def test_deprecated__version__(self):
with self.assertWarnsRegex(
DeprecationWarning,
"'__version__' is deprecated and slated for removal in Python 3.20",
) as cm:
getattr(ipaddress, "__version__")
self.assertEqual(cm.filename, __file__)
if __name__ == '__main__':
unittest.main()
+38
View File
@@ -1,6 +1,9 @@
import netrc, os, unittest, sys, textwrap
from pathlib import Path
from test import support
from test.support import os_helper
from unittest.mock import patch
temp_filename = os_helper.TESTFN
@@ -59,6 +62,9 @@ class NetrcTestCase(unittest.TestCase):
"machine host.domain.com login",
"machine host.domain.com account",
"machine host.domain.com password",
"machine host.domain.com login \"\"",
"machine host.domain.com account \"\"",
"machine host.domain.com password \"\"",
"machine host.domain.com login \"\" account",
"machine host.domain.com login \"\" password",
"machine host.domain.com account \"\" password"
@@ -71,6 +77,9 @@ class NetrcTestCase(unittest.TestCase):
"default login",
"default account",
"default password",
"default login \"\"",
"default account \"\"",
"default password \"\"",
"default login \"\" account",
"default login \"\" password",
"default account \"\" password"
@@ -79,6 +88,15 @@ class NetrcTestCase(unittest.TestCase):
nrc = self.make_nrc(item)
self.assertEqual(nrc.hosts['default'], ('', '', ''))
def test_empty_quoted_token_is_not_eof(self):
data = (
'"" invalid',
'machine host.domain.com "" invalid',
)
for item in data:
with self.subTest(item=item):
self.assertRaises(netrc.NetrcParseError, self.make_nrc, item)
def test_invalid_tokens(self):
data = (
"invalid host.domain.com",
@@ -309,6 +327,26 @@ class NetrcTestCase(unittest.TestCase):
self.assertEqual(nrc.hosts['foo.domain.com'],
('anonymous', '', 'pass'))
@unittest.skipUnless(os.name == 'posix', 'POSIX only test')
@unittest.skipUnless(hasattr(os, 'getuid'), "os.getuid is required")
@os_helper.skip_unless_working_chmod
def test_security_only_once(self):
# Make sure security check is only run once per parse when multiple
# entries are found.
with patch.object(netrc.netrc, "_security_check") as mock:
with os_helper.temp_dir() as tmp_dir:
netrc_path = Path(tmp_dir) / '.netrc'
netrc_path.write_text("""\
machine foo.domain.com login bar password pass
machine bar.domain.com login foo password pass
""")
netrc_path.chmod(0o600)
with os_helper.EnvironmentVarGuard() as environ:
environ.set('HOME', tmp_dir)
netrc.netrc()
mock.assert_called_once()
if __name__ == "__main__":
unittest.main()
+19
View File
@@ -792,6 +792,25 @@ class TestPlistlib(unittest.TestCase):
})
self.assertIsNot(pl2['first'], pl2['second'])
def test_frozendict(self):
pl = frozendict(
aString="Doodah",
anInt=728,
aDict=frozendict(
anotherString="hello",
aTrueValue=True,
),
aList=["A", "B", 12],
)
for fmt in ALL_FORMATS:
with self.subTest(fmt=fmt):
data = plistlib.dumps(pl, fmt=fmt)
pl2 = plistlib.loads(data)
self.assertEqual(pl2, dict(pl))
self.assertIsInstance(pl2, dict)
self.assertIsInstance(pl2['aDict'], dict)
def test_controlcharacters(self):
for i in range(128):
c = chr(i)
+2 -4
View File
@@ -2496,8 +2496,7 @@ class TestKDE(unittest.TestCase):
data = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2, 7.8, 14.3, 15.1, 15.3, 15.8, 17.0]
xarr = [x / 10 for x in range(-100, 250)]
# TODO: RUSTPYTHON - n originally 1_000_000 in CPython implementation but sorting is slow
n = 30_000
n = 1_000_000
h = 1.75
dx = 0.1
@@ -2519,8 +2518,7 @@ class TestKDE(unittest.TestCase):
F_hat = statistics.kde(data, h, kernel, cumulative=True)
for x in xarr:
# TODO : RUSTPYTHON - abs_tol=0.0005 in CPython implementation but smaller `n` increases variance
self.assertTrue(math.isclose(p_observed(x), p_expected(x), abs_tol=0.005))
self.assertTrue(math.isclose(p_observed(x), p_expected(x), abs_tol=0.0005))
# Test online updates to data