Skip to content

Update unittest from CPython 3.10.5 #3862

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jul 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
67 changes: 47 additions & 20 deletions Lib/doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def _test():
import sys
import traceback
import unittest
from io import StringIO
from io import StringIO # XXX: RUSTPYTHON; , IncrementalNewlineDecoder
from collections import namedtuple

TestResults = namedtuple('TestResults', 'failed attempted')
Expand Down Expand Up @@ -211,17 +211,28 @@ def _normalize_module(module, depth=2):
else:
raise TypeError("Expected a module, string, or None")

def _newline_convert(data):
# The IO module provides a handy decoder for universal newline conversion
return IncrementalNewlineDecoder(None, True).decode(data, True)

def _load_testfile(filename, package, module_relative, encoding):
if module_relative:
package = _normalize_module(package, 3)
filename = _module_relative_path(package, filename)
if getattr(package, '__loader__', None) is not None:
if hasattr(package.__loader__, 'get_data'):
file_contents = package.__loader__.get_data(filename)
file_contents = file_contents.decode(encoding)
# get_data() opens files as 'rb', so one must do the equivalent
# conversion as universal newlines would do.
return file_contents.replace(os.linesep, '\n'), filename
if (loader := getattr(package, '__loader__', None)) is None:
try:
loader = package.__spec__.loader
except AttributeError:
pass
if hasattr(loader, 'get_data'):
file_contents = loader.get_data(filename)
file_contents = file_contents.decode(encoding)
# get_data() opens files as 'rb', so one must do the equivalent
# conversion as universal newlines would do.

# TODO: RUSTPYTHON; use _newline_convert once io.IncrementalNewlineDecoder is implemented
return file_contents.replace(os.linesep, '\n'), filename
# return _newline_convert(file_contents), filename
with open(filename, encoding=encoding) as f:
return f.read(), filename

Expand Down Expand Up @@ -965,6 +976,17 @@ def _from_module(self, module, object):
else:
raise ValueError("object must be a class or function")

def _is_routine(self, obj):
"""
Safely unwrap objects and determine if they are functions.
"""
maybe_routine = obj
try:
maybe_routine = inspect.unwrap(maybe_routine)
except ValueError:
pass
return inspect.isroutine(maybe_routine)

def _find(self, tests, obj, name, module, source_lines, globs, seen):
"""
Find tests for the given object and any contained objects, and
Expand All @@ -987,9 +1009,9 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen):
if inspect.ismodule(obj) and self._recurse:
for valname, val in obj.__dict__.items():
valname = '%s.%s' % (name, valname)

# Recurse to functions & classes.
if ((inspect.isroutine(inspect.unwrap(val))
or inspect.isclass(val)) and
if ((self._is_routine(val) or inspect.isclass(val)) and
self._from_module(module, val)):
self._find(tests, val, valname, module, source_lines,
globs, seen)
Expand All @@ -1015,10 +1037,8 @@ def _find(self, tests, obj, name, module, source_lines, globs, seen):
if inspect.isclass(obj) and self._recurse:
for valname, val in obj.__dict__.items():
# Special handling for staticmethod/classmethod.
if isinstance(val, staticmethod):
val = getattr(obj, valname)
if isinstance(val, classmethod):
val = getattr(obj, valname).__func__
if isinstance(val, (staticmethod, classmethod)):
val = val.__func__

# Recurse to methods, properties, and nested classes.
if ((inspect.isroutine(val) or inspect.isclass(val) or
Expand Down Expand Up @@ -1068,19 +1088,21 @@ def _get_test(self, obj, name, module, globs, source_lines):

def _find_lineno(self, obj, source_lines):
"""
Return a line number of the given object's docstring. Note:
this method assumes that the object has a docstring.
Return a line number of the given object's docstring.

Returns `None` if the given object does not have a docstring.
"""
lineno = None
docstring = getattr(obj, '__doc__', None)

# Find the line number for modules.
if inspect.ismodule(obj):
if inspect.ismodule(obj) and docstring is not None:
lineno = 0

# Find the line number for classes.
# Note: this could be fooled if a class is defined multiple
# times in a single file.
if inspect.isclass(obj):
if inspect.isclass(obj) and docstring is not None:
if source_lines is None:
return None
pat = re.compile(r'^\s*class\s*%s\b' %
Expand All @@ -1092,7 +1114,9 @@ def _find_lineno(self, obj, source_lines):

# Find the line number for functions & methods.
if inspect.ismethod(obj): obj = obj.__func__
if inspect.isfunction(obj): obj = obj.__code__
if inspect.isfunction(obj) and getattr(obj, '__doc__', None):
# We don't use `docstring` var here, because `obj` can be changed.
obj = obj.__code__
if inspect.istraceback(obj): obj = obj.tb_frame
if inspect.isframe(obj): obj = obj.f_code
if inspect.iscode(obj):
Expand Down Expand Up @@ -1327,7 +1351,7 @@ def __run(self, test, compileflags, out):
try:
# Don't blink! This is where the user's code gets run.
exec(compile(example.source, filename, "single",
compileflags, 1), test.globs)
compileflags, True), test.globs)
self.debugger.set_continue() # ==== Example Finished ====
exception = None
except KeyboardInterrupt:
Expand Down Expand Up @@ -2154,6 +2178,7 @@ def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
unittest.TestCase.__init__(self)
self._dt_optionflags = optionflags
self._dt_checker = checker
self._dt_globs = test.globs.copy()
self._dt_test = test
self._dt_setUp = setUp
self._dt_tearDown = tearDown
Expand All @@ -2170,7 +2195,9 @@ def tearDown(self):
if self._dt_tearDown is not None:
self._dt_tearDown(test)

# restore the original globs
test.globs.clear()
test.globs.update(self._dt_globs)

def runTest(self):
test = self._dt_test
Expand Down
70 changes: 47 additions & 23 deletions Lib/test/test_support.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import contextlib
import errno
import importlib
import io
Expand All @@ -12,6 +11,8 @@
import textwrap
import time
import unittest
import warnings

from test import support
from test.support import import_helper
from test.support import os_helper
Expand All @@ -23,10 +24,38 @@


class TestSupport(unittest.TestCase):
@classmethod
def setUpClass(cls):
orig_filter_len = len(warnings.filters)
cls._warnings_helper_token = support.ignore_deprecations_from(
"test.support.warnings_helper", like=".*used in test_support.*"
)
cls._test_support_token = support.ignore_deprecations_from(
"test.test_support", like=".*You should NOT be seeing this.*"
)
assert len(warnings.filters) == orig_filter_len + 2

@classmethod
def tearDownClass(cls):
orig_filter_len = len(warnings.filters)
support.clear_ignored_deprecations(
cls._warnings_helper_token,
cls._test_support_token,
)
assert len(warnings.filters) == orig_filter_len - 2

def test_ignored_deprecations_are_silent(self):
"""Test support.ignore_deprecations_from() silences warnings"""
with warnings.catch_warnings(record=True) as warning_objs:
warnings_helper._warn_about_deprecation()
warnings.warn("You should NOT be seeing this.", DeprecationWarning)
messages = [str(w.message) for w in warning_objs]
self.assertEqual(len(messages), 0, messages)

def test_import_module(self):
import_helper.import_module("ftplib")
self.assertRaises(unittest.SkipTest, import_helper.import_module, "foo")
self.assertRaises(unittest.SkipTest,
import_helper.import_module, "foo")

def test_import_fresh_module(self):
import_helper.import_fresh_module("ftplib")
Expand All @@ -47,7 +76,7 @@ def test_unload(self):
self.assertNotIn("sched", sys.modules)

def test_unlink(self):
with open(TESTFN, "w") as f:
with open(TESTFN, "w", encoding="utf-8") as f:
pass
os_helper.unlink(TESTFN)
self.assertFalse(os.path.exists(TESTFN))
Expand Down Expand Up @@ -79,7 +108,7 @@ def test_rmtree(self):

def test_forget(self):
mod_filename = TESTFN + '.py'
with open(mod_filename, 'w') as f:
with open(mod_filename, 'w', encoding="utf-8") as f:
print('foo = 1', file=f)
sys.path.insert(0, os.curdir)
importlib.invalidate_caches()
Expand Down Expand Up @@ -177,16 +206,14 @@ def test_temp_dir__forked_child(self):
script_helper.assert_python_ok("-c", textwrap.dedent("""
import os
from test import support
from test.support import os_helper
with os_helper.temp_cwd() as temp_path:
pid = os.fork()
if pid != 0:
# parent process (child has pid == 0)
# parent process

# wait for the child to terminate
(pid, status) = os.waitpid(pid, 0)
if status != 0:
raise AssertionError(f"Child process failed with exit "
f"status indication 0x{status:x}.")
support.wait_process(pid, exitcode=0)

# Make sure that temp_path is still present. When the child
# process leaves the 'temp_cwd'-context, the __exit__()-
Expand Down Expand Up @@ -295,8 +322,8 @@ def test_check_syntax_error(self):

def test_CleanImport(self):
import importlib
with import_helper.CleanImport("asyncore"):
importlib.import_module("asyncore")
with import_helper.CleanImport("pprint"):
importlib.import_module("pprint")

def test_DirsOnSysPath(self):
with import_helper.DirsOnSysPath('foo', 'bar'):
Expand Down Expand Up @@ -392,6 +419,8 @@ def test_detect_api_mismatch__ignore(self):
self.OtherClass, self.RefClass, ignore=ignore)
self.assertEqual(set(), missing_items)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_check__all__(self):
extra = {'tempdir'}
not_exported = {'template'}
Expand All @@ -414,8 +443,10 @@ def test_check__all__(self):

self.assertRaises(AssertionError, support.check__all__, self, unittest)

@unittest.skipUnless(hasattr(os, 'waitpid') and hasattr(os, 'WNOHANG') and hasattr(os, 'fork'),
'need os.waitpid() and os.WNOHANG and os.fork()')
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.skipUnless(hasattr(os, 'waitpid') and hasattr(os, 'WNOHANG'),
'need os.waitpid() and os.WNOHANG')
def test_reap_children(self):
# Make sure that there is no other pending child process
support.reap_children()
Expand All @@ -427,7 +458,7 @@ def test_reap_children(self):
os._exit(0)

t0 = time.monotonic()
deadline = time.monotonic() + 60.0
deadline = time.monotonic() + support.SHORT_TIMEOUT

was_altered = support.environment_altered
try:
Expand Down Expand Up @@ -502,7 +533,6 @@ def test_args_from_interpreter_flags(self):
['-Wignore', '-X', 'dev'],
['-X', 'faulthandler'],
['-X', 'importtime'],
['-X', 'showalloccount'],
['-X', 'showrefcount'],
['-X', 'tracemalloc'],
['-X', 'tracemalloc=3'],
Expand Down Expand Up @@ -647,7 +677,7 @@ def test_fd_count(self):
def check_print_warning(self, msg, expected):
stderr = io.StringIO()

old_stderr = sys.__stderr__
old_stderr = sys.__stderr__
try:
sys.__stderr__ = stderr
support.print_warning(msg)
Expand All @@ -671,7 +701,6 @@ def test_print_warning(self):
# findfile
# check_warnings
# EnvironmentVarGuard
# TransientResource
# transient_internet
# run_with_locale
# set_memlimit
Expand All @@ -682,15 +711,10 @@ def test_print_warning(self):
# run_doctest
# threading_cleanup
# reap_threads
# strip_python_stderr
# can_symlink
# skip_unless_symlink
# SuppressCrashReport


def test_main():
tests = [TestSupport]
support.run_unittest(*tests)

if __name__ == '__main__':
test_main()
unittest.main()
12 changes: 6 additions & 6 deletions Lib/test/test_unittest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
from test import support


def test_main():
# used by regrtest
support.run_unittest(unittest.test.suite())
support.reap_children()

def load_tests(*_):
# used by unittest
return unittest.test.suite()


def tearDownModule():
support.reap_children()


if __name__ == "__main__":
test_main()
unittest.main()
Loading