mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Align collections and testing libraries with Python 3.15 (#9192)
* Align collections and testing libraries with Python 3.15 Refresh twelve production modules and canonical tests through scripts/update_lib at CPython d220409a13b77de7a6c30abe16ea90db01528edc. Preserve existing RustPython unittest helpers and test guards. Accept frozendict in native defaultdict and OrderedDict union slots, matching upstream PyAnyDict_Check without changing constructors, factories, or update order. Validated as a local stack on the exact #9189 regex/dataclasses/typing prerequisite; this dependency avoids eager typing.ByteString deprecation during unittest warning tests. Assisted-by: OpenAI Codex: model version unavailable * Synchronize regression runner with doctest subtest reporting Refresh the canonical test_regrtest consumer and its direct runner/pythoninfo closure through scripts/update_lib at CPython d220409a13b77de7a6c30abe16ea90db01528edc. Preserve both existing expected-failure markers. This aligns the runner test with doctest reporting two failed examples and one skipped example. Assisted-by: OpenAI Codex: model version unavailable
This commit is contained in:
1 parent
e7e3dfd6ce
commit
feca6f2004
33 files changed
+2316
-745
No files matched your search
Vendored
+12
-1
@@ -49,7 +49,7 @@ __all__ = ["Awaitable", "Coroutine",
|
||||
"Mapping", "MutableMapping",
|
||||
"MappingView", "KeysView", "ItemsView", "ValuesView",
|
||||
"Sequence", "MutableSequence",
|
||||
"ByteString", "Buffer",
|
||||
"Buffer",
|
||||
]
|
||||
|
||||
# This module has been renamed from collections.abc to _collections_abc to
|
||||
@@ -823,6 +823,7 @@ class Mapping(Collection):
|
||||
|
||||
__reversed__ = None
|
||||
|
||||
Mapping.register(frozendict)
|
||||
Mapping.register(mappingproxy)
|
||||
Mapping.register(framelocalsproxy)
|
||||
|
||||
@@ -1170,3 +1171,13 @@ class MutableSequence(Sequence):
|
||||
|
||||
MutableSequence.register(list)
|
||||
MutableSequence.register(bytearray)
|
||||
|
||||
_deprecated_ByteString = globals().pop("ByteString")
|
||||
|
||||
def __getattr__(attr):
|
||||
if attr == "ByteString":
|
||||
import warnings
|
||||
warnings._deprecated("collections.abc.ByteString", remove=(3, 17))
|
||||
globals()["ByteString"] = _deprecated_ByteString
|
||||
return _deprecated_ByteString
|
||||
raise AttributeError(f"module 'collections.abc' has no attribute {attr!r}")
|
||||
Vendored
+73
-18
@@ -32,6 +32,8 @@ import sys as _sys
|
||||
_sys.modules['collections.abc'] = _collections_abc
|
||||
abc = _collections_abc
|
||||
|
||||
lazy from copy import copy as _copy
|
||||
lazy from heapq import nlargest as _nlargest
|
||||
from itertools import chain as _chain
|
||||
from itertools import repeat as _repeat
|
||||
from itertools import starmap as _starmap
|
||||
@@ -59,8 +61,6 @@ try:
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
heapq = None # Lazily imported
|
||||
|
||||
|
||||
################################################################################
|
||||
### OrderedDict
|
||||
@@ -328,14 +328,14 @@ class OrderedDict(dict):
|
||||
return self
|
||||
|
||||
def __or__(self, other):
|
||||
if not isinstance(other, dict):
|
||||
if not isinstance(other, (dict, frozendict)):
|
||||
return NotImplemented
|
||||
new = self.__class__(self)
|
||||
new.update(other)
|
||||
return new
|
||||
|
||||
def __ror__(self, other):
|
||||
if not isinstance(other, dict):
|
||||
if not isinstance(other, (dict, frozendict)):
|
||||
return NotImplemented
|
||||
new = self.__class__(other)
|
||||
new.update(self)
|
||||
@@ -637,12 +637,7 @@ class Counter(dict):
|
||||
if n is None:
|
||||
return sorted(self.items(), key=_itemgetter(1), reverse=True)
|
||||
|
||||
# Lazy import to speedup Python startup time
|
||||
global heapq
|
||||
if heapq is None:
|
||||
import heapq
|
||||
|
||||
return heapq.nlargest(n, self.items(), key=_itemgetter(1))
|
||||
return _nlargest(n, self.items(), key=_itemgetter(1))
|
||||
|
||||
def elements(self):
|
||||
'''Iterator over elements repeating each as many times as its count.
|
||||
@@ -779,23 +774,27 @@ class Counter(dict):
|
||||
# When the multiplicities are all zero or one, multiset operations
|
||||
# are guaranteed to be equivalent to the corresponding operations
|
||||
# for regular sets.
|
||||
#
|
||||
# Given counter multisets such as:
|
||||
# cp = Counter(a=1, b=0, c=1)
|
||||
# cq = Counter(c=1, d=0, e=1)
|
||||
#
|
||||
# The corresponding regular sets would be:
|
||||
# sp = {'a', 'c'}
|
||||
# sq = {'c', 'e'}
|
||||
#
|
||||
# All of the following relations would hold:
|
||||
# set(cp + cq) == sp | sq
|
||||
# set(cp - cq) == sp - sq
|
||||
# set(cp | cq) == sp | sq
|
||||
# set(cp & cq) == sp & sq
|
||||
# (cp == cq) == (sp == sq)
|
||||
# (cp != cq) == (sp != sq)
|
||||
# (cp <= cq) == (sp <= sq)
|
||||
# (cp < cq) == (sp < sq)
|
||||
# (cp >= cq) == (sp >= sq)
|
||||
# (cp > cq) == (sp > sq)
|
||||
# set(cp + cq) == sp | sq
|
||||
# set(cp - cq) == sp - sq
|
||||
# set(cp | cq) == sp | sq
|
||||
# set(cp & cq) == sp & sq
|
||||
# set(cp ^ cq) == sp ^ sq
|
||||
|
||||
def __eq__(self, other):
|
||||
'True if all counts agree. Missing counts are treated as zero.'
|
||||
@@ -908,6 +907,33 @@ class Counter(dict):
|
||||
result[elem] = newcount
|
||||
return result
|
||||
|
||||
def __xor__(self, other):
|
||||
'''Symmetric difference. Absolute value of count differences.
|
||||
|
||||
The symmetric difference p ^ q is equivalent to:
|
||||
|
||||
(p - q) | (q - p).
|
||||
|
||||
For each element, symmetric difference gives the same result as:
|
||||
|
||||
max(p[elem], q[elem]) - min(p[elem], q[elem])
|
||||
|
||||
>>> Counter(a=5, b=3, c=2, d=2) ^ Counter(a=1, b=3, c=5, e=1)
|
||||
Counter({'a': 4, 'c': 3, 'd': 2, 'e': 1})
|
||||
|
||||
'''
|
||||
if not isinstance(other, Counter):
|
||||
return NotImplemented
|
||||
result = Counter()
|
||||
for elem, count in self.items():
|
||||
newcount = abs(count - other[elem])
|
||||
if newcount:
|
||||
result[elem] = newcount
|
||||
for elem, count in other.items():
|
||||
if elem not in self and count:
|
||||
result[elem] = abs(count)
|
||||
return result
|
||||
|
||||
def __pos__(self):
|
||||
'Adds an empty counter, effectively stripping negative and zero counts'
|
||||
result = Counter()
|
||||
@@ -990,6 +1016,22 @@ class Counter(dict):
|
||||
self[elem] = other_count
|
||||
return self._keep_positive()
|
||||
|
||||
def __ixor__(self, other):
|
||||
'''Inplace symmetric difference. Absolute value of count differences.
|
||||
|
||||
>>> c = Counter(a=5, b=3, c=2, d=2)
|
||||
>>> c ^= Counter(a=1, b=3, c=5, e=1)
|
||||
>>> c
|
||||
Counter({'a': 4, 'c': 3, 'd': 2, 'e': 1})
|
||||
|
||||
'''
|
||||
for elem, count in self.items():
|
||||
self[elem] = abs(count - other[elem])
|
||||
for elem, count in other.items():
|
||||
if elem not in self:
|
||||
self[elem] = abs(count)
|
||||
return self._keep_positive()
|
||||
|
||||
|
||||
########################################################################
|
||||
### ChainMap
|
||||
@@ -1177,14 +1219,14 @@ class UserDict(_collections_abc.MutableMapping):
|
||||
def __or__(self, other):
|
||||
if isinstance(other, UserDict):
|
||||
return self.__class__(self.data | other.data)
|
||||
if isinstance(other, dict):
|
||||
if isinstance(other, (dict, frozendict)):
|
||||
return self.__class__(self.data | other)
|
||||
return NotImplemented
|
||||
|
||||
def __ror__(self, other):
|
||||
if isinstance(other, UserDict):
|
||||
return self.__class__(other.data | self.data)
|
||||
if isinstance(other, dict):
|
||||
if isinstance(other, (dict, frozendict)):
|
||||
return self.__class__(other | self.data)
|
||||
return NotImplemented
|
||||
|
||||
@@ -1205,16 +1247,29 @@ class UserDict(_collections_abc.MutableMapping):
|
||||
def copy(self):
|
||||
if self.__class__ is UserDict:
|
||||
return UserDict(self.data.copy())
|
||||
import copy
|
||||
data = self.data
|
||||
try:
|
||||
self.data = {}
|
||||
c = copy.copy(self)
|
||||
c = _copy(self)
|
||||
finally:
|
||||
self.data = data
|
||||
c.update(self)
|
||||
return c
|
||||
|
||||
|
||||
# This method has a default implementation in MutableMapping, but dict's
|
||||
# equivalent is last-in, first-out instead of first-in, first-out.
|
||||
def popitem(self):
|
||||
"""Remove and return a (key, value) pair as a 2-tuple.
|
||||
|
||||
Removes pairs in the same order as the wrapped mapping's popitem()
|
||||
method. For dict objects (the default), that order is last-in,
|
||||
first-out (LIFO).
|
||||
Raises KeyError if the UserDict is empty.
|
||||
"""
|
||||
return self.data.popitem()
|
||||
|
||||
|
||||
@classmethod
|
||||
def fromkeys(cls, iterable, value=None):
|
||||
d = cls()
|
||||
|
||||
Vendored
+113
-68
@@ -1,11 +1,17 @@
|
||||
"""Utilities for with-statement contexts. See PEP 343."""
|
||||
|
||||
import abc
|
||||
import os
|
||||
import sys
|
||||
import _collections_abc
|
||||
from collections import deque
|
||||
from functools import wraps
|
||||
from types import MethodType, GenericAlias
|
||||
lazy from inspect import (
|
||||
isasyncgenfunction as _isasyncgenfunction,
|
||||
iscoroutinefunction as _iscoroutinefunction,
|
||||
isgeneratorfunction as _isgeneratorfunction,
|
||||
)
|
||||
from types import GenericAlias
|
||||
|
||||
__all__ = ["asynccontextmanager", "contextmanager", "closing", "nullcontext",
|
||||
"AbstractContextManager", "AbstractAsyncContextManager",
|
||||
@@ -79,11 +85,37 @@ class ContextDecorator(object):
|
||||
return self
|
||||
|
||||
def __call__(self, func):
|
||||
@wraps(func)
|
||||
def inner(*args, **kwds):
|
||||
with self._recreate_cm():
|
||||
return func(*args, **kwds)
|
||||
return inner
|
||||
wrapper = wraps(func)
|
||||
if _isasyncgenfunction(func):
|
||||
|
||||
async def asyncgen_inner(*args, **kwds):
|
||||
with self._recreate_cm():
|
||||
async with aclosing(func(*args, **kwds)) as gen:
|
||||
async for value in gen:
|
||||
yield value
|
||||
|
||||
return wrapper(asyncgen_inner)
|
||||
elif _iscoroutinefunction(func):
|
||||
|
||||
async def async_inner(*args, **kwds):
|
||||
with self._recreate_cm():
|
||||
return await func(*args, **kwds)
|
||||
|
||||
return wrapper(async_inner)
|
||||
elif _isgeneratorfunction(func):
|
||||
|
||||
def gen_inner(*args, **kwds):
|
||||
with self._recreate_cm(), closing(func(*args, **kwds)) as gen:
|
||||
return (yield from gen)
|
||||
|
||||
return wrapper(gen_inner)
|
||||
else:
|
||||
|
||||
def inner(*args, **kwds):
|
||||
with self._recreate_cm():
|
||||
return func(*args, **kwds)
|
||||
|
||||
return wrapper(inner)
|
||||
|
||||
|
||||
class AsyncContextDecorator(object):
|
||||
@@ -95,11 +127,41 @@ class AsyncContextDecorator(object):
|
||||
return self
|
||||
|
||||
def __call__(self, func):
|
||||
@wraps(func)
|
||||
async def inner(*args, **kwds):
|
||||
async with self._recreate_cm():
|
||||
return await func(*args, **kwds)
|
||||
return inner
|
||||
wrapper = wraps(func)
|
||||
if _isasyncgenfunction(func):
|
||||
|
||||
async def asyncgen_inner(*args, **kwds):
|
||||
async with (
|
||||
self._recreate_cm(),
|
||||
aclosing(func(*args, **kwds)) as gen
|
||||
):
|
||||
async for value in gen:
|
||||
yield value
|
||||
|
||||
return wrapper(asyncgen_inner)
|
||||
elif _iscoroutinefunction(func):
|
||||
|
||||
async def async_inner(*args, **kwds):
|
||||
async with self._recreate_cm():
|
||||
return await func(*args, **kwds)
|
||||
|
||||
return wrapper(async_inner)
|
||||
elif _isgeneratorfunction(func):
|
||||
|
||||
async def gen_inner(*args, **kwds):
|
||||
async with self._recreate_cm():
|
||||
with closing(func(*args, **kwds)) as gen:
|
||||
for value in gen:
|
||||
yield value
|
||||
|
||||
return wrapper(gen_inner)
|
||||
else:
|
||||
|
||||
async def inner(*args, **kwds):
|
||||
async with self._recreate_cm():
|
||||
return func(*args, **kwds)
|
||||
|
||||
return wrapper(inner)
|
||||
|
||||
|
||||
class _GeneratorContextManagerBase:
|
||||
@@ -469,13 +531,23 @@ class suppress(AbstractContextManager):
|
||||
return False
|
||||
|
||||
|
||||
def _lookup_special(obj, name, default):
|
||||
# Follow the standard lookup behaviour for special methods.
|
||||
from inspect import getattr_static, _descriptor_get
|
||||
cls = type(obj)
|
||||
try:
|
||||
descr = getattr_static(cls, name)
|
||||
except AttributeError:
|
||||
return default
|
||||
return _descriptor_get(descr, obj)
|
||||
|
||||
|
||||
_sentinel = ['SENTINEL']
|
||||
|
||||
|
||||
class _BaseExitStack:
|
||||
"""A base class for ExitStack and AsyncExitStack."""
|
||||
|
||||
@staticmethod
|
||||
def _create_exit_wrapper(cm, cm_exit):
|
||||
return MethodType(cm_exit, cm)
|
||||
|
||||
@staticmethod
|
||||
def _create_cb_wrapper(callback, /, *args, **kwds):
|
||||
def _exit_wrapper(exc_type, exc, tb):
|
||||
@@ -499,17 +571,8 @@ class _BaseExitStack:
|
||||
Also accepts any object with an __exit__ method (registering a call
|
||||
to the method instead of the object itself).
|
||||
"""
|
||||
# We use an unbound method rather than a bound method to follow
|
||||
# the standard lookup behaviour for special methods.
|
||||
_cb_type = type(exit)
|
||||
|
||||
try:
|
||||
exit_method = _cb_type.__exit__
|
||||
except AttributeError:
|
||||
# Not a context manager, so assume it's a callable.
|
||||
self._push_exit_callback(exit)
|
||||
else:
|
||||
self._push_cm_exit(exit, exit_method)
|
||||
exit_method = _lookup_special(exit, '__exit__', exit)
|
||||
self._push_exit_callback(exit_method)
|
||||
return exit # Allow use as a decorator.
|
||||
|
||||
def enter_context(self, cm):
|
||||
@@ -518,17 +581,18 @@ class _BaseExitStack:
|
||||
If successful, also pushes its __exit__ method as a callback and
|
||||
returns the result of the __enter__ method.
|
||||
"""
|
||||
# We look up the special methods on the type to match the with
|
||||
# statement.
|
||||
cls = type(cm)
|
||||
try:
|
||||
_enter = cls.__enter__
|
||||
_exit = cls.__exit__
|
||||
except AttributeError:
|
||||
_enter = _lookup_special(cm, '__enter__', _sentinel)
|
||||
if _enter is _sentinel:
|
||||
cls = type(cm)
|
||||
raise TypeError(f"'{cls.__module__}.{cls.__qualname__}' object does "
|
||||
f"not support the context manager protocol") from None
|
||||
result = _enter(cm)
|
||||
self._push_cm_exit(cm, _exit)
|
||||
f"not support the context manager protocol")
|
||||
_exit = _lookup_special(cm, '__exit__', _sentinel)
|
||||
if _exit is _sentinel:
|
||||
cls = type(cm)
|
||||
raise TypeError(f"'{cls.__module__}.{cls.__qualname__}' object does "
|
||||
f"not support the context manager protocol")
|
||||
result = _enter()
|
||||
self._push_exit_callback(_exit)
|
||||
return result
|
||||
|
||||
def callback(self, callback, /, *args, **kwds):
|
||||
@@ -544,11 +608,6 @@ class _BaseExitStack:
|
||||
self._push_exit_callback(_exit_wrapper)
|
||||
return callback # Allow use as a decorator
|
||||
|
||||
def _push_cm_exit(self, cm, cm_exit):
|
||||
"""Helper to correctly register callbacks to __exit__ methods."""
|
||||
_exit_wrapper = self._create_exit_wrapper(cm, cm_exit)
|
||||
self._push_exit_callback(_exit_wrapper, True)
|
||||
|
||||
def _push_exit_callback(self, callback, is_sync=True):
|
||||
self._exit_callbacks.append((is_sync, callback))
|
||||
|
||||
@@ -641,10 +700,6 @@ class AsyncExitStack(_BaseExitStack, AbstractAsyncContextManager):
|
||||
# connection later in the list raise an exception.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _create_async_exit_wrapper(cm, cm_exit):
|
||||
return MethodType(cm_exit, cm)
|
||||
|
||||
@staticmethod
|
||||
def _create_async_cb_wrapper(callback, /, *args, **kwds):
|
||||
async def _exit_wrapper(exc_type, exc, tb):
|
||||
@@ -657,16 +712,18 @@ class AsyncExitStack(_BaseExitStack, AbstractAsyncContextManager):
|
||||
If successful, also pushes its __aexit__ method as a callback and
|
||||
returns the result of the __aenter__ method.
|
||||
"""
|
||||
cls = type(cm)
|
||||
try:
|
||||
_enter = cls.__aenter__
|
||||
_exit = cls.__aexit__
|
||||
except AttributeError:
|
||||
_enter = _lookup_special(cm, '__aenter__', _sentinel)
|
||||
if _enter is _sentinel:
|
||||
cls = type(cm)
|
||||
raise TypeError(f"'{cls.__module__}.{cls.__qualname__}' object does "
|
||||
f"not support the asynchronous context manager protocol"
|
||||
) from None
|
||||
result = await _enter(cm)
|
||||
self._push_async_cm_exit(cm, _exit)
|
||||
f"not support the asynchronous context manager protocol")
|
||||
_exit = _lookup_special(cm, '__aexit__', _sentinel)
|
||||
if _exit is _sentinel:
|
||||
cls = type(cm)
|
||||
raise TypeError(f"'{cls.__module__}.{cls.__qualname__}' object does "
|
||||
f"not support the asynchronous context manager protocol")
|
||||
result = await _enter()
|
||||
self._push_exit_callback(_exit, False)
|
||||
return result
|
||||
|
||||
def push_async_exit(self, exit):
|
||||
@@ -677,14 +734,8 @@ class AsyncExitStack(_BaseExitStack, AbstractAsyncContextManager):
|
||||
Also accepts any object with an __aexit__ method (registering a call
|
||||
to the method instead of the object itself).
|
||||
"""
|
||||
_cb_type = type(exit)
|
||||
try:
|
||||
exit_method = _cb_type.__aexit__
|
||||
except AttributeError:
|
||||
# Not an async context manager, so assume it's a coroutine function
|
||||
self._push_exit_callback(exit, False)
|
||||
else:
|
||||
self._push_async_cm_exit(exit, exit_method)
|
||||
exit_method = _lookup_special(exit, '__aexit__', exit)
|
||||
self._push_exit_callback(exit_method, False)
|
||||
return exit # Allow use as a decorator
|
||||
|
||||
def push_async_callback(self, callback, /, *args, **kwds):
|
||||
@@ -704,12 +755,6 @@ class AsyncExitStack(_BaseExitStack, AbstractAsyncContextManager):
|
||||
"""Immediately unwind the context stack."""
|
||||
await self.__aexit__(None, None, None)
|
||||
|
||||
def _push_async_cm_exit(self, cm, cm_exit):
|
||||
"""Helper to correctly register coroutine function to __aexit__
|
||||
method."""
|
||||
_exit_wrapper = self._create_async_exit_wrapper(cm, cm_exit)
|
||||
self._push_exit_callback(_exit_wrapper, False)
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
|
||||
Vendored
+73
-34
@@ -33,6 +33,7 @@ __all__ = ['get_close_matches', 'ndiff', 'restore', 'SequenceMatcher',
|
||||
from heapq import nlargest as _nlargest
|
||||
from collections import namedtuple as _namedtuple
|
||||
from types import GenericAlias
|
||||
lazy from _colorize import can_colorize, get_theme
|
||||
|
||||
Match = _namedtuple('Match', 'a b size')
|
||||
|
||||
@@ -637,15 +638,15 @@ class SequenceMatcher:
|
||||
# avail[x] is the number of times x appears in 'b' less the
|
||||
# number of times we've seen it in 'a' so far ... kinda
|
||||
avail = {}
|
||||
availhas, matches = avail.__contains__, 0
|
||||
matches = 0
|
||||
for elt in self.a:
|
||||
if availhas(elt):
|
||||
if elt in avail:
|
||||
numb = avail[elt]
|
||||
else:
|
||||
numb = fullbcount.get(elt, 0)
|
||||
avail[elt] = numb - 1
|
||||
if numb > 0:
|
||||
matches = matches + 1
|
||||
matches += 1
|
||||
return _calculate_ratio(matches, len(self.a) + len(self.b))
|
||||
|
||||
def real_quick_ratio(self):
|
||||
@@ -701,10 +702,12 @@ def get_close_matches(word, possibilities, n=3, cutoff=0.6):
|
||||
s.set_seq2(word)
|
||||
for x in possibilities:
|
||||
s.set_seq1(x)
|
||||
if s.real_quick_ratio() >= cutoff and \
|
||||
s.quick_ratio() >= cutoff and \
|
||||
s.ratio() >= cutoff:
|
||||
result.append((s.ratio(), x))
|
||||
if s.real_quick_ratio() < cutoff or s.quick_ratio() < cutoff:
|
||||
continue
|
||||
|
||||
ratio = s.ratio()
|
||||
if ratio >= cutoff:
|
||||
result.append((ratio, x))
|
||||
|
||||
# Move the best scorers to head of list
|
||||
result = _nlargest(n, result)
|
||||
@@ -939,10 +942,12 @@ class Differ:
|
||||
cruncher.set_seq1(a[i])
|
||||
# Ordering by cheapest to most expensive ratio is very
|
||||
# valuable, most often getting out early.
|
||||
if (crqr() > best_ratio
|
||||
and cqr() > best_ratio
|
||||
and cr() > best_ratio):
|
||||
best_i, best_j, best_ratio = i, j, cr()
|
||||
if crqr() <= best_ratio or cqr() <= best_ratio:
|
||||
continue
|
||||
|
||||
ratio = cr()
|
||||
if ratio > best_ratio:
|
||||
best_i, best_j, best_ratio = i, j, ratio
|
||||
|
||||
if best_i is None:
|
||||
# found nothing to synch on yet - move to next j
|
||||
@@ -1094,7 +1099,7 @@ def _format_range_unified(start, stop):
|
||||
return '{},{}'.format(beginning, length)
|
||||
|
||||
def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
|
||||
tofiledate='', n=3, lineterm='\n'):
|
||||
tofiledate='', n=3, lineterm='\n', *, color=False):
|
||||
r"""
|
||||
Compare two sequences of lines; generate the delta as a unified diff.
|
||||
|
||||
@@ -1111,6 +1116,10 @@ def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
|
||||
For inputs that do not have trailing newlines, set the lineterm
|
||||
argument to "" so that the output will be uniformly newline free.
|
||||
|
||||
Set 'color' to True to enable output in color, similar to
|
||||
'git diff --color'. Even if enabled, it can be
|
||||
controlled using environment variables such as 'NO_COLOR'.
|
||||
|
||||
The unidiff format normally has a header for filenames and modification
|
||||
times. Any or all of these may be specified using strings for
|
||||
'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
|
||||
@@ -1134,6 +1143,11 @@ def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
|
||||
four
|
||||
"""
|
||||
|
||||
if color and can_colorize():
|
||||
t = get_theme(force_color=True).difflib
|
||||
else:
|
||||
t = get_theme(force_no_color=True).difflib
|
||||
|
||||
_check_types(a, b, fromfile, tofile, fromfiledate, tofiledate, lineterm)
|
||||
started = False
|
||||
for group in SequenceMatcher(None,a,b).get_grouped_opcodes(n):
|
||||
@@ -1141,25 +1155,25 @@ def unified_diff(a, b, fromfile='', tofile='', fromfiledate='',
|
||||
started = True
|
||||
fromdate = '\t{}'.format(fromfiledate) if fromfiledate else ''
|
||||
todate = '\t{}'.format(tofiledate) if tofiledate else ''
|
||||
yield '--- {}{}{}'.format(fromfile, fromdate, lineterm)
|
||||
yield '+++ {}{}{}'.format(tofile, todate, lineterm)
|
||||
yield f'{t.header}--- {fromfile}{fromdate}{lineterm}{t.reset}'
|
||||
yield f'{t.header}+++ {tofile}{todate}{lineterm}{t.reset}'
|
||||
|
||||
first, last = group[0], group[-1]
|
||||
file1_range = _format_range_unified(first[1], last[2])
|
||||
file2_range = _format_range_unified(first[3], last[4])
|
||||
yield '@@ -{} +{} @@{}'.format(file1_range, file2_range, lineterm)
|
||||
yield f'{t.hunk}@@ -{file1_range} +{file2_range} @@{lineterm}{t.reset}'
|
||||
|
||||
for tag, i1, i2, j1, j2 in group:
|
||||
if tag == 'equal':
|
||||
for line in a[i1:i2]:
|
||||
yield ' ' + line
|
||||
yield f'{t.context} {line}{t.reset}'
|
||||
continue
|
||||
if tag in {'replace', 'delete'}:
|
||||
for line in a[i1:i2]:
|
||||
yield '-' + line
|
||||
yield f'{t.removed}-{line}{t.reset}'
|
||||
if tag in {'replace', 'insert'}:
|
||||
for line in b[j1:j2]:
|
||||
yield '+' + line
|
||||
yield f'{t.added}+{line}{t.reset}'
|
||||
|
||||
|
||||
########################################################################
|
||||
@@ -1615,16 +1629,13 @@ def _mdiff(fromlines, tolines, context=None, linejunk=None,
|
||||
|
||||
|
||||
_file_template = """
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type"
|
||||
content="text/html; charset=%(charset)s" />
|
||||
<title></title>
|
||||
<style type="text/css">%(styles)s
|
||||
<meta charset="%(charset)s">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Diff comparison</title>
|
||||
<style>%(styles)s
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -1636,13 +1647,36 @@ _file_template = """
|
||||
|
||||
_styles = """
|
||||
:root {color-scheme: light dark}
|
||||
table.diff {font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; border:medium}
|
||||
.diff_header {background-color:#e0e0e0}
|
||||
td.diff_header {text-align:right}
|
||||
.diff_next {background-color:#c0c0c0}
|
||||
table.diff {
|
||||
font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace;
|
||||
border: medium;
|
||||
}
|
||||
.diff_header {
|
||||
background-color: #e0e0e0;
|
||||
font-weight: bold;
|
||||
}
|
||||
td.diff_header {
|
||||
text-align: right;
|
||||
padding: 0 8px;
|
||||
}
|
||||
.diff_next {
|
||||
background-color: #c0c0c0;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.diff_add {background-color:palegreen}
|
||||
.diff_chg {background-color:#ffff77}
|
||||
.diff_sub {background-color:#ffaaaa}
|
||||
table.diff[summary="Legends"] {
|
||||
margin-top: 20px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
table.diff[summary="Legends"] th {
|
||||
background-color: #e0e0e0;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
table.diff[summary="Legends"] td {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.diff_header {background-color:#666}
|
||||
@@ -1650,6 +1684,8 @@ _styles = """
|
||||
.diff_add {background-color:darkgreen}
|
||||
.diff_chg {background-color:#847415}
|
||||
.diff_sub {background-color:darkred}
|
||||
table.diff[summary="Legends"] {border-color:#555}
|
||||
table.diff[summary="Legends"] th{background-color:#666}
|
||||
}"""
|
||||
|
||||
_table_template = """
|
||||
@@ -1692,7 +1728,7 @@ class HtmlDiff(object):
|
||||
make_table -- generates HTML for a single side by side table
|
||||
make_file -- generates complete HTML file with a single side by side table
|
||||
|
||||
See tools/scripts/diff.py for an example usage of this class.
|
||||
See Doc/includes/diff.py for an example usage of this class.
|
||||
"""
|
||||
|
||||
_file_template = _file_template
|
||||
@@ -1892,8 +1928,11 @@ class HtmlDiff(object):
|
||||
# make space non-breakable so they don't get compressed or line wrapped
|
||||
text = text.replace(' ',' ').rstrip()
|
||||
|
||||
return '<td class="diff_header"%s>%s</td><td nowrap="nowrap">%s</td>' \
|
||||
% (id,linenum,text)
|
||||
# add a class to the td tag if there is a difference on the line
|
||||
css_class = ' class="diff_changed" ' if flag else ' '
|
||||
|
||||
return f'<td class="diff_header"{id}>{linenum}</td>' \
|
||||
+ f'<td{css_class}nowrap="nowrap">{text}</td>'
|
||||
|
||||
def _make_prefix(self):
|
||||
"""Create unique anchor prefixes"""
|
||||
|
||||
Vendored
+109
-33
@@ -102,15 +102,14 @@ import pdb
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
import types
|
||||
import unittest
|
||||
from io import StringIO, IncrementalNewlineDecoder
|
||||
from io import StringIO, TextIOWrapper, BytesIO
|
||||
from collections import namedtuple
|
||||
import _colorize # Used in doctests
|
||||
from _colorize import ANSIColors, can_colorize
|
||||
lazy import _colorize # Used in doctests
|
||||
lazy from _colorize import ANSIColors, can_colorize
|
||||
|
||||
|
||||
__unittest = True
|
||||
|
||||
class TestResults(namedtuple('TestResults', 'failed attempted')):
|
||||
def __new__(cls, failed, attempted, *, skipped=0):
|
||||
results = super().__new__(cls, failed, attempted)
|
||||
@@ -238,10 +237,6 @@ 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)
|
||||
@@ -253,10 +248,9 @@ def _load_testfile(filename, package, module_relative, encoding):
|
||||
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.
|
||||
return _newline_convert(file_contents), filename
|
||||
return TextIOWrapper(BytesIO(file_contents), encoding=encoding, newline=None).read(), filename
|
||||
with open(filename, encoding=encoding) as f:
|
||||
return f.read(), filename
|
||||
|
||||
@@ -388,7 +382,7 @@ class _OutputRedirectingPdb(pdb.Pdb):
|
||||
self.__out = out
|
||||
self.__debugger_used = False
|
||||
# do not play signal games in the pdb
|
||||
pdb.Pdb.__init__(self, stdout=out, nosigint=True)
|
||||
super().__init__(stdout=out, nosigint=True)
|
||||
# still use input() to get user input
|
||||
self.use_rawinput = 1
|
||||
|
||||
@@ -928,7 +922,7 @@ class DocTestFinder:
|
||||
# given object's docstring.
|
||||
try:
|
||||
file = inspect.getsourcefile(obj)
|
||||
except TypeError:
|
||||
except (TypeError, OSError):
|
||||
source_lines = None
|
||||
else:
|
||||
if not file:
|
||||
@@ -1173,6 +1167,32 @@ class DocTestFinder:
|
||||
if pat.match(source_lines[lineno]):
|
||||
return lineno
|
||||
|
||||
# Handle __test__ string doctests formatted as triple-quoted
|
||||
# strings. Find a non-blank line in the test string and match it
|
||||
# in the source, verifying subsequent lines also match to handle
|
||||
# duplicate lines.
|
||||
if isinstance(obj, str) and source_lines is not None:
|
||||
obj_lines = obj.splitlines(keepends=True)
|
||||
# Skip the first line (may be on same line as opening quotes)
|
||||
# and any blank lines to find a meaningful line to match.
|
||||
start_index = 1
|
||||
while (start_index < len(obj_lines)
|
||||
and not obj_lines[start_index].strip()):
|
||||
start_index += 1
|
||||
if start_index < len(obj_lines):
|
||||
target_line = obj_lines[start_index]
|
||||
for lineno, source_line in enumerate(source_lines):
|
||||
if source_line == target_line:
|
||||
# Verify subsequent lines also match
|
||||
for i in range(start_index + 1, len(obj_lines) - 1):
|
||||
source_idx = lineno + i - start_index
|
||||
if source_idx >= len(source_lines):
|
||||
break
|
||||
if obj_lines[i] != source_lines[source_idx]:
|
||||
break
|
||||
else:
|
||||
return lineno - start_index
|
||||
|
||||
# We couldn't find the line number.
|
||||
return None
|
||||
|
||||
@@ -1283,6 +1303,11 @@ class DocTestRunner:
|
||||
# Reporting methods
|
||||
#/////////////////////////////////////////////////////////////////
|
||||
|
||||
def report_skip(self, out, test, example):
|
||||
"""
|
||||
Report that the given example was skipped.
|
||||
"""
|
||||
|
||||
def report_start(self, out, test, example):
|
||||
"""
|
||||
Report that the test runner is about to process the given
|
||||
@@ -1380,6 +1405,8 @@ class DocTestRunner:
|
||||
|
||||
# If 'SKIP' is set, then skip this example.
|
||||
if self.optionflags & SKIP:
|
||||
if not quiet:
|
||||
self.report_skip(out, test, example)
|
||||
skips += 1
|
||||
continue
|
||||
|
||||
@@ -2277,12 +2304,63 @@ def set_unittest_reportflags(flags):
|
||||
return old
|
||||
|
||||
|
||||
class _DocTestCaseRunner(DocTestRunner):
|
||||
|
||||
def __init__(self, *args, test_case, test_result, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._test_case = test_case
|
||||
self._test_result = test_result
|
||||
self._examplenum = 0
|
||||
|
||||
def _subTest(self):
|
||||
subtest = unittest.case._SubTest(self._test_case, str(self._examplenum), {})
|
||||
self._examplenum += 1
|
||||
return subtest
|
||||
|
||||
def report_skip(self, out, test, example):
|
||||
unittest.case._addSkip(self._test_result, self._subTest(), '')
|
||||
|
||||
def report_success(self, out, test, example, got):
|
||||
self._test_result.addSubTest(self._test_case, self._subTest(), None)
|
||||
|
||||
def report_unexpected_exception(self, out, test, example, exc_info):
|
||||
tb = self._add_traceback(exc_info[2], test, example)
|
||||
exc_info = (*exc_info[:2], tb)
|
||||
self._test_result.addSubTest(self._test_case, self._subTest(), exc_info)
|
||||
|
||||
def report_failure(self, out, test, example, got):
|
||||
msg = ('Failed example:\n' + _indent(example.source) +
|
||||
self._checker.output_difference(example, got, self.optionflags).rstrip('\n'))
|
||||
exc = self._test_case.failureException(msg)
|
||||
tb = self._add_traceback(None, test, example)
|
||||
exc_info = (type(exc), exc, tb)
|
||||
self._test_result.addSubTest(self._test_case, self._subTest(), exc_info)
|
||||
|
||||
def _add_traceback(self, traceback, test, example):
|
||||
if test.lineno is None or example.lineno is None:
|
||||
lineno = None
|
||||
else:
|
||||
lineno = test.lineno + example.lineno + 1
|
||||
return types.SimpleNamespace(
|
||||
tb_frame = types.SimpleNamespace(
|
||||
f_globals=test.globs,
|
||||
f_code=types.SimpleNamespace(
|
||||
co_filename=test.filename,
|
||||
co_name=test.name,
|
||||
),
|
||||
),
|
||||
tb_next = traceback,
|
||||
tb_lasti = -1,
|
||||
tb_lineno = lineno,
|
||||
)
|
||||
|
||||
|
||||
class DocTestCase(unittest.TestCase):
|
||||
|
||||
def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
|
||||
checker=None):
|
||||
|
||||
unittest.TestCase.__init__(self)
|
||||
super().__init__()
|
||||
self._dt_optionflags = optionflags
|
||||
self._dt_checker = checker
|
||||
self._dt_test = test
|
||||
@@ -2306,30 +2384,28 @@ class DocTestCase(unittest.TestCase):
|
||||
test.globs.clear()
|
||||
test.globs.update(self._dt_globs)
|
||||
|
||||
def run(self, result=None):
|
||||
self._test_result = result
|
||||
return super().run(result)
|
||||
|
||||
def runTest(self):
|
||||
test = self._dt_test
|
||||
old = sys.stdout
|
||||
new = StringIO()
|
||||
optionflags = self._dt_optionflags
|
||||
result = self._test_result
|
||||
|
||||
if not (optionflags & REPORTING_FLAGS):
|
||||
# The option flags don't include any reporting flags,
|
||||
# so add the default reporting flags
|
||||
optionflags |= _unittest_reportflags
|
||||
if getattr(result, 'failfast', False):
|
||||
optionflags |= FAIL_FAST
|
||||
|
||||
runner = DocTestRunner(optionflags=optionflags,
|
||||
checker=self._dt_checker, verbose=False)
|
||||
|
||||
try:
|
||||
runner.DIVIDER = "-"*70
|
||||
results = runner.run(test, out=new.write, clear_globs=False)
|
||||
if results.skipped == results.attempted:
|
||||
raise unittest.SkipTest("all examples were skipped")
|
||||
finally:
|
||||
sys.stdout = old
|
||||
|
||||
if results.failed:
|
||||
raise self.failureException(self.format_failure(new.getvalue().rstrip('\n')))
|
||||
runner = _DocTestCaseRunner(optionflags=optionflags,
|
||||
checker=self._dt_checker, verbose=False,
|
||||
test_case=self, test_result=result)
|
||||
results = runner.run(test, clear_globs=False)
|
||||
if results.skipped == results.attempted:
|
||||
raise unittest.SkipTest("all examples were skipped")
|
||||
|
||||
def format_failure(self, err):
|
||||
test = self._dt_test
|
||||
@@ -2444,7 +2520,7 @@ class DocTestCase(unittest.TestCase):
|
||||
class SkipDocTestCase(DocTestCase):
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
DocTestCase.__init__(self, None)
|
||||
super().__init__(None)
|
||||
|
||||
def setUp(self):
|
||||
self.skipTest("DocTestSuite will not work with -O2 and above")
|
||||
@@ -2875,7 +2951,7 @@ __test__ = {"_TestClass": _TestClass,
|
||||
def _test():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="doctest runner", color=True)
|
||||
parser = argparse.ArgumentParser(description="doctest runner")
|
||||
parser.add_argument('-v', '--verbose', action='store_true', default=False,
|
||||
help='print very verbose output for all tests')
|
||||
parser.add_argument('-o', '--option', action='append',
|
||||
@@ -2885,8 +2961,8 @@ def _test():
|
||||
' than once to apply multiple options'))
|
||||
parser.add_argument('-f', '--fail-fast', action='store_true',
|
||||
help=('stop running tests after first failure (this'
|
||||
' is a shorthand for -o FAIL_FAST, and is'
|
||||
' in addition to any other -o options)'))
|
||||
' is a shorthand for `-o FAIL_FAST`, and is'
|
||||
' in addition to any other `-o` options)'))
|
||||
parser.add_argument('file', nargs='+',
|
||||
help='file containing the tests to run')
|
||||
args = parser.parse_args()
|
||||
|
||||
Vendored
+62
-60
@@ -15,73 +15,73 @@ DESCRIPTION = """\
|
||||
Run Python regression tests.
|
||||
|
||||
If no arguments or options are provided, finds all files matching
|
||||
the pattern "test_*" in the Lib/test subdirectory and runs
|
||||
them in alphabetical order (but see -M and -u, below, for exceptions).
|
||||
the pattern `test_*` in the `Lib/test` subdirectory and runs
|
||||
them in alphabetical order (but see `-M` and `-u`, below, for exceptions).
|
||||
|
||||
For more rigorous testing, it is useful to use the following
|
||||
command line:
|
||||
|
||||
python -E -Wd -m test [options] [test_name1 ...]
|
||||
`python -E -Wd -m test [options] [test_name1 ...]`
|
||||
"""
|
||||
|
||||
EPILOG = """\
|
||||
Additional option details:
|
||||
|
||||
-r randomizes test execution order. You can use --randseed=int to provide an
|
||||
int seed value for the randomizer. The randseed value will be used
|
||||
`-r` randomizes test execution order. You can use `--randseed=int` to provide an
|
||||
int seed value for the randomizer. The `randseed` value will be used
|
||||
to set seeds for all random usages in tests
|
||||
(including randomizing the tests order if -r is set).
|
||||
(including randomizing the tests order if `-r` is set).
|
||||
By default we always set random seed, but do not randomize test order.
|
||||
|
||||
-s On the first invocation of regrtest using -s, the first test file found
|
||||
`-s` On the first invocation of regrtest using `-s`, the first test file found
|
||||
or the first test file given on the command line is run, and the name of
|
||||
the next test is recorded in a file named pynexttest. If run from the
|
||||
Python build directory, pynexttest is located in the 'build' subdirectory,
|
||||
otherwise it is located in tempfile.gettempdir(). On subsequent runs,
|
||||
the test in pynexttest is run, and the next test is written to pynexttest.
|
||||
When the last test has been run, pynexttest is deleted. In this way it
|
||||
the next test is recorded in a file named `pynexttest`. If run from the
|
||||
Python build directory, `pynexttest` is located in the 'build' subdirectory,
|
||||
otherwise it is located in `tempfile.gettempdir()`. On subsequent runs,
|
||||
the test in `pynexttest` is run, and the next test is written to `pynexttest`.
|
||||
When the last test has been run, `pynexttest` is deleted. In this way it
|
||||
is possible to single step through the test files. This is useful when
|
||||
doing memory analysis on the Python interpreter, which process tends to
|
||||
consume too many resources to run the full regression test non-stop.
|
||||
|
||||
-S is used to resume running tests after an interrupted run. It will
|
||||
maintain the order a standard run (i.e. it assumes -r is not used).
|
||||
`-S` is used to resume running tests after an interrupted run. It will
|
||||
maintain the order a standard run (i.e. it assumes `-r` is not used).
|
||||
This is useful after the tests have prematurely stopped for some external
|
||||
reason and you want to resume the run from where you left off rather
|
||||
than starting from the beginning. Note: this is different from --prioritize.
|
||||
than starting from the beginning. Note: this is different from `--prioritize`.
|
||||
|
||||
--prioritize is used to influence the order of selected tests, such that
|
||||
`--prioritize` is used to influence the order of selected tests, such that
|
||||
the tests listed as an argument are executed first. This is especially
|
||||
useful when combined with -j and -r to pin the longest-running tests
|
||||
to start at the beginning of a test run. Pass --prioritize=test_a,test_b
|
||||
to make test_a run first, followed by test_b, and then the other tests.
|
||||
If test_a wasn't selected for execution by regular means, --prioritize will
|
||||
useful when combined with `-j` and `-r` to pin the longest-running tests
|
||||
to start at the beginning of a test run. Pass `--prioritize=test_a,test_b`
|
||||
to make `test_a` run first, followed by `test_b`, and then the other tests.
|
||||
If test_a wasn't selected for execution by regular means, `--prioritize` will
|
||||
not make it execute.
|
||||
|
||||
-f reads the names of tests from the file given as f's argument, one
|
||||
`-f` reads the names of tests from the file given as `f`'s argument, one
|
||||
or more test names per line. Whitespace is ignored. Blank lines and
|
||||
lines beginning with '#' are ignored. This is especially useful for
|
||||
lines beginning with `#` are ignored. This is especially useful for
|
||||
whittling down failures involving interactions among tests.
|
||||
|
||||
-L causes the leaks(1) command to be run just before exit if it exists.
|
||||
leaks(1) is available on Mac OS X and presumably on some other
|
||||
`-L` causes the leaks(1) command to be run just before exit if it exists.
|
||||
leaks(1) is available on macOS and presumably on some other
|
||||
FreeBSD-derived systems.
|
||||
|
||||
-R runs each test several times and examines sys.gettotalrefcount() to
|
||||
`-R` runs each test several times and examines `sys.gettotalrefcount()` to
|
||||
see if the test appears to be leaking references. The argument should
|
||||
be of the form stab:run:fname where 'stab' is the number of times the
|
||||
test is run to let gettotalrefcount settle down, 'run' is the number
|
||||
of times further it is run and 'fname' is the name of the file the
|
||||
reports are written to. These parameters all have defaults (5, 4 and
|
||||
"reflog.txt" respectively), and the minimal invocation is '-R :'.
|
||||
be of the form `stab:run:fname` where `stab` is the number of times the
|
||||
test is run to let gettotalrefcount settle down, `run` is the number
|
||||
of times further it is run and `fname` is the name of the file the
|
||||
reports are written to. These parameters all have defaults (`5`, `4` and
|
||||
`"reflog.txt"` respectively), and the minimal invocation is `-R :`.
|
||||
|
||||
-M runs tests that require an exorbitant amount of memory. These tests
|
||||
`-M` runs tests that require an exorbitant amount of memory. These tests
|
||||
typically try to ascertain containers keep working when containing more than
|
||||
2 billion objects, which only works on 64-bit systems. There are also some
|
||||
tests that try to exhaust the address space of the process, which only makes
|
||||
sense on 32-bit systems with at least 2Gb of memory. The passed-in memlimit,
|
||||
which is a string in the form of '2.5Gb', determines how much memory the
|
||||
tests will limit themselves to (but they may go slightly over.) The number
|
||||
which is a string in the form of `'2.5Gb'`, determines how much memory the
|
||||
tests will limit themselves to (but they may go slightly over). The number
|
||||
shouldn't be more memory than the machine has (including swap memory). You
|
||||
should also keep in mind that swap memory is generally much, much slower
|
||||
than RAM, and setting memlimit to all available RAM or higher will heavily
|
||||
@@ -90,7 +90,7 @@ limit of less than 2.5Gb, and many require more than 20Gb. Tests that expect
|
||||
to use more than memlimit memory will be skipped. The big-memory tests
|
||||
generally run very, very long.
|
||||
|
||||
-u is used to specify which special resource intensive tests to run,
|
||||
`-u` is used to specify which special resource intensive tests to run,
|
||||
such as those requiring large file support or network connectivity.
|
||||
The argument is a comma-separated list of words indicating the
|
||||
resources to test. Currently only the following are defined:
|
||||
@@ -137,16 +137,16 @@ resources to test. Currently only the following are defined:
|
||||
wantobjects - Allows to run Tkinter tests with the specified value of
|
||||
tkinter.wantobjects.
|
||||
|
||||
To enable all resources except one, use '-uall,-<resource>'. For
|
||||
example, to run all the tests except for the gui tests, give the
|
||||
option '-uall,-gui'.
|
||||
To enable all resources except one, use `-uall,-<resource>`. For
|
||||
example, to run all the tests except for the `gui` tests, give the
|
||||
option `-uall,-gui`.
|
||||
|
||||
--matchfile filters tests using a text file, one pattern per line.
|
||||
`--matchfile` filters tests using a text file, one pattern per line.
|
||||
Pattern examples:
|
||||
|
||||
- test method: test_stat_attributes
|
||||
- test class: FileTests
|
||||
- test identifier: test_os.FileTests.test_stat_attributes
|
||||
- test method: `test_stat_attributes`
|
||||
- test class: `FileTests`
|
||||
- test identifier: `test_os.FileTests.test_stat_attributes`
|
||||
"""
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ def _create_parser():
|
||||
'buildbot workers')
|
||||
group.add_argument('--timeout', metavar='TIMEOUT',
|
||||
help='dump the traceback and exit if a test takes '
|
||||
'more than TIMEOUT seconds; disabled if TIMEOUT '
|
||||
'more than `TIMEOUT` seconds; disabled if `TIMEOUT` '
|
||||
'is negative or equals to zero')
|
||||
group.add_argument('--wait', action='store_true',
|
||||
help='wait for user input, e.g., allow a debugger '
|
||||
@@ -261,11 +261,11 @@ def _create_parser():
|
||||
|
||||
group = parser.add_argument_group('Verbosity')
|
||||
group.add_argument('-v', '--verbose', action='count',
|
||||
help='run tests in verbose mode with output to stdout')
|
||||
help='run tests in verbose mode with output to `stdout`')
|
||||
group.add_argument('-w', '--rerun', action='store_true',
|
||||
help='re-run failed tests in verbose mode')
|
||||
group.add_argument('--verbose2', action='store_true', dest='rerun',
|
||||
help='deprecated alias to --rerun')
|
||||
help='deprecated alias to `--rerun`')
|
||||
group.add_argument('-W', '--verbose3', action='store_true',
|
||||
help='display test output on failure')
|
||||
group.add_argument('-q', '--quiet', action='store_true',
|
||||
@@ -295,22 +295,22 @@ def _create_parser():
|
||||
more_details)
|
||||
group.add_argument('-m', '--match', metavar='PAT',
|
||||
dest='match_tests', action=FilterAction, const=True,
|
||||
help='match test cases and methods with glob pattern PAT')
|
||||
help='match test cases and methods with glob pattern `PAT`')
|
||||
group.add_argument('-i', '--ignore', metavar='PAT',
|
||||
dest='match_tests', action=FilterAction, const=False,
|
||||
help='ignore test cases and methods with glob pattern PAT')
|
||||
help='ignore test cases and methods with glob pattern `PAT`')
|
||||
group.add_argument('--matchfile', metavar='FILENAME',
|
||||
dest='match_tests',
|
||||
action=FromFileFilterAction, const=True,
|
||||
help='similar to --match but get patterns from a '
|
||||
help='similar to `--match` but get patterns from a '
|
||||
'text file, one pattern per line')
|
||||
group.add_argument('--ignorefile', metavar='FILENAME',
|
||||
dest='match_tests',
|
||||
action=FromFileFilterAction, const=False,
|
||||
help='similar to --matchfile but it receives patterns '
|
||||
help='similar to `--matchfile` but it receives patterns '
|
||||
'from text file to ignore')
|
||||
group.add_argument('-G', '--failfast', action='store_true',
|
||||
help='fail as soon as a test fails (only with -v or -W)')
|
||||
help='fail as soon as a test fails (only with `-v` or `-W`)')
|
||||
group.add_argument('-u', '--use', metavar='RES1,RES2,...',
|
||||
action='extend', type=resources_list,
|
||||
help='specify which special resource intensive tests '
|
||||
@@ -325,7 +325,7 @@ def _create_parser():
|
||||
|
||||
group = parser.add_argument_group('Special runs')
|
||||
group.add_argument('-L', '--runleaks', action='store_true',
|
||||
help='run the leaks(1) command just before exit.' +
|
||||
help='run the `leaks(1)` command just before exit.' +
|
||||
more_details)
|
||||
group.add_argument('-R', '--huntrleaks', metavar='RUNCOUNTS',
|
||||
type=huntrleaks,
|
||||
@@ -333,20 +333,20 @@ def _create_parser():
|
||||
'very slow).' + more_details)
|
||||
group.add_argument('-j', '--multiprocess', metavar='PROCESSES',
|
||||
dest='use_mp', type=int,
|
||||
help='run PROCESSES processes at once')
|
||||
help='run `PROCESSES` processes at once')
|
||||
group.add_argument('--single-process', action='store_true',
|
||||
dest='single_process',
|
||||
help='always run all tests sequentially in '
|
||||
'a single process, ignore -jN option, '
|
||||
'a single process, ignore `-jN` option, '
|
||||
'and failed tests are also rerun sequentially '
|
||||
'in the same process')
|
||||
group.add_argument('--parallel-threads', metavar='PARALLEL_THREADS',
|
||||
type=int,
|
||||
help='run copies of each test in PARALLEL_THREADS at '
|
||||
help='run copies of each test in `PARALLEL_THREADS` at '
|
||||
'once')
|
||||
group.add_argument('-T', '--coverage', action='store_true',
|
||||
dest='trace',
|
||||
help='turn on code coverage tracing using the trace '
|
||||
help='turn on code coverage tracing using the `trace` '
|
||||
'module')
|
||||
group.add_argument('-D', '--coverdir', metavar='DIR',
|
||||
type=relative_filename,
|
||||
@@ -356,18 +356,18 @@ def _create_parser():
|
||||
help='put coverage files alongside modules')
|
||||
group.add_argument('-t', '--threshold', metavar='THRESHOLD',
|
||||
type=int,
|
||||
help='call gc.set_threshold(THRESHOLD)')
|
||||
help='call `gc.set_threshold(THRESHOLD)`')
|
||||
group.add_argument('-n', '--nowindows', action='store_true',
|
||||
help='suppress error message boxes on Windows')
|
||||
group.add_argument('-F', '--forever', action='store_true',
|
||||
help='run the specified tests in a loop, until an '
|
||||
'error happens; imply --failfast')
|
||||
'error happens; imply `--failfast`')
|
||||
group.add_argument('--list-tests', action='store_true',
|
||||
help="only write the name of tests that will be run, "
|
||||
"don't execute them")
|
||||
group.add_argument('--list-cases', action='store_true',
|
||||
help='only write the name of test cases that will be run'
|
||||
' , don\'t execute them')
|
||||
help='only write the name of test cases that will be run, '
|
||||
'don\'t execute them')
|
||||
group.add_argument('-P', '--pgo', dest='pgo', action='store_true',
|
||||
help='enable Profile Guided Optimization (PGO) training')
|
||||
group.add_argument('--pgo-extended', action='store_true',
|
||||
@@ -390,9 +390,11 @@ def _create_parser():
|
||||
group.add_argument('--tempdir', metavar='PATH',
|
||||
help='override the working directory for the test run')
|
||||
group.add_argument('--cleanup', action='store_true',
|
||||
help='remove old test_python_* directories')
|
||||
help='remove old `test_python_*` directories')
|
||||
group.add_argument('--bisect', action='store_true',
|
||||
help='if some tests fail, run test.bisect_cmd on them')
|
||||
help='if some tests fail, run `test.bisect_cmd` on them')
|
||||
group.add_argument('--pythoninfo', action='store_true',
|
||||
help="run `python -m test.pythoninfo` before tests")
|
||||
group.add_argument('--dont-add-python-opts', dest='_add_python_opts',
|
||||
action='store_false',
|
||||
help="internal option, don't use it")
|
||||
|
||||
Vendored
+16
-7
@@ -26,7 +26,7 @@ from .utils import (
|
||||
strip_py_suffix, count, format_duration,
|
||||
printlist, get_temp_dir, get_work_dir, exit_timeout,
|
||||
display_header, cleanup_temp_dir, print_warning,
|
||||
is_cross_compiled, get_host_runner,
|
||||
is_cross_compiled, get_host_runner, display_title,
|
||||
EXIT_TIMEOUT)
|
||||
|
||||
|
||||
@@ -126,6 +126,7 @@ class Regrtest:
|
||||
self.coverage: bool = ns.trace
|
||||
self.coverage_dir: StrPath | None = ns.coverdir
|
||||
self._tmp_dir: StrPath | None = ns.tempdir
|
||||
self.pythoninfo: bool = ns.pythoninfo
|
||||
|
||||
# Randomize
|
||||
self.randomize: bool = ns.randomize
|
||||
@@ -322,9 +323,7 @@ class Regrtest:
|
||||
title = f"Bisect {test}"
|
||||
if progress:
|
||||
title = f"{title} ({progress})"
|
||||
print(title)
|
||||
print("#" * len(title))
|
||||
print()
|
||||
display_title(title)
|
||||
|
||||
cmd = runtests.create_python_cmd()
|
||||
cmd.extend([
|
||||
@@ -345,9 +344,7 @@ class Regrtest:
|
||||
exitcode = proc.returncode
|
||||
|
||||
title = f"{title}: exit code {exitcode}"
|
||||
print(title)
|
||||
print("#" * len(title))
|
||||
print(flush=True)
|
||||
display_title(title)
|
||||
|
||||
if exitcode:
|
||||
print(f"Bisect failed with exit code {exitcode}")
|
||||
@@ -752,6 +749,15 @@ class Regrtest:
|
||||
)
|
||||
return self._tmp_dir
|
||||
|
||||
def run_pythoninfo(self):
|
||||
from test import pythoninfo
|
||||
try:
|
||||
pythoninfo.main()
|
||||
except SystemExit:
|
||||
# Ignore non-zero exit code on purpose
|
||||
pass
|
||||
print()
|
||||
|
||||
def main(self, tests: TestList | None = None) -> NoReturn:
|
||||
if self.want_add_python_opts:
|
||||
self._add_python_opts()
|
||||
@@ -765,6 +771,9 @@ class Regrtest:
|
||||
if self.want_wait:
|
||||
input("Press any key to continue...")
|
||||
|
||||
if self.pythoninfo:
|
||||
self.run_pythoninfo()
|
||||
|
||||
setup_test_dir(self.test_dir)
|
||||
selected, tests = self.find_tests(tests)
|
||||
|
||||
|
||||
Vendored
+27
-1
@@ -118,7 +118,8 @@ def printlist(x, width=70, indent=4, file=None):
|
||||
blanks = ' ' * indent
|
||||
# Print the sorted list: 'x' may be a '--random' list or a set()
|
||||
print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width,
|
||||
initial_indent=blanks, subsequent_indent=blanks),
|
||||
initial_indent=blanks, subsequent_indent=blanks,
|
||||
break_long_words=False, break_on_hyphens=False),
|
||||
file=file)
|
||||
|
||||
|
||||
@@ -294,6 +295,25 @@ def clear_caches():
|
||||
else:
|
||||
importlib_metadata.FastPath.__new__.cache_clear()
|
||||
|
||||
try:
|
||||
encodings = sys.modules['encodings']
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
encodings._cache.clear()
|
||||
|
||||
try:
|
||||
codecs = sys.modules['codecs']
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
# There's no direct API to clear the codecs search cache, but
|
||||
# `unregister` clears it implicitly.
|
||||
def noop_search_function(name):
|
||||
return None
|
||||
codecs.register(noop_search_function)
|
||||
codecs.unregister(noop_search_function)
|
||||
|
||||
|
||||
def get_build_info():
|
||||
# Get most important configure and build options as a list of strings.
|
||||
@@ -727,3 +747,9 @@ def _sanitize_xml_replace(regs):
|
||||
|
||||
def sanitize_xml(text: str) -> str:
|
||||
return ILLEGAL_XML_CHARS_RE.sub(_sanitize_xml_replace, text)
|
||||
|
||||
|
||||
def display_title(title):
|
||||
print(title)
|
||||
print("#" * len(title))
|
||||
print(flush=True)
|
||||
Vendored
+414
-98
@@ -8,6 +8,12 @@ import traceback
|
||||
import warnings
|
||||
|
||||
|
||||
MS_WINDOWS = (sys.platform == "win32")
|
||||
APPLE = (sys.platform in ("darwin", "ios", "tvos", "watchos"))
|
||||
|
||||
COMMAND_TIMEOUT = 60.0
|
||||
|
||||
|
||||
def normalize_text(text):
|
||||
if text is None:
|
||||
return None
|
||||
@@ -16,6 +22,25 @@ def normalize_text(text):
|
||||
return text.strip()
|
||||
|
||||
|
||||
def first_line(text):
|
||||
# Get the first line. Return text unchanged if it's empty.
|
||||
lines = text.splitlines()
|
||||
if lines:
|
||||
return lines[0]
|
||||
else:
|
||||
# text is an empty string
|
||||
return text
|
||||
|
||||
|
||||
def read_first_line(filename):
|
||||
# Get the first line of a text file and strip trailing spaces
|
||||
try:
|
||||
with open(filename, encoding="utf-8") as fp:
|
||||
return fp.readline().rstrip()
|
||||
except OSError:
|
||||
return ''
|
||||
|
||||
|
||||
class PythonInfo:
|
||||
def __init__(self):
|
||||
self.info = {}
|
||||
@@ -146,6 +171,8 @@ def collect_sys(info_add):
|
||||
text = 'No (sys.getobjects() missing)'
|
||||
info_add('build.Py_TRACE_REFS', text)
|
||||
|
||||
info_add('sys.is_remote_debug_enabled', sys.is_remote_debug_enabled())
|
||||
|
||||
|
||||
def collect_platform(info_add):
|
||||
import platform
|
||||
@@ -279,9 +306,11 @@ def collect_os(info_add):
|
||||
"BUILDPYTHON",
|
||||
"CC",
|
||||
"CFLAGS",
|
||||
"CI",
|
||||
"COLUMNS",
|
||||
"COMPUTERNAME",
|
||||
"COMSPEC",
|
||||
"CONTAINER",
|
||||
"CPP",
|
||||
"CPPFLAGS",
|
||||
"DISPLAY",
|
||||
@@ -289,11 +318,14 @@ def collect_os(info_add):
|
||||
"DISTUTILS_USE_SDK",
|
||||
"DYLD_LIBRARY_PATH",
|
||||
"ENSUREPIP_OPTIONS",
|
||||
"FORCE_COLOR",
|
||||
"GITHUB_ACTIONS",
|
||||
"HISTORY_FILE",
|
||||
"HOME",
|
||||
"HOMEDRIVE",
|
||||
"HOMEPATH",
|
||||
"IDLESTARTUP",
|
||||
"IMAGE_OS_VERSION",
|
||||
"IPHONEOS_DEPLOYMENT_TARGET",
|
||||
"LANG",
|
||||
"LDFLAGS",
|
||||
@@ -305,11 +337,13 @@ def collect_os(info_add):
|
||||
"MAKEFLAGS",
|
||||
"MIXERDEV",
|
||||
"MSSDK",
|
||||
"NO_COLOR",
|
||||
"PATH",
|
||||
"PATHEXT",
|
||||
"PIP_CONFIG_FILE",
|
||||
"PLAT",
|
||||
"POSIXLY_CORRECT",
|
||||
"PYTHON_COLORS",
|
||||
"PY_SAX_PARSER",
|
||||
"ProgramFiles",
|
||||
"ProgramFiles(x86)",
|
||||
@@ -416,24 +450,53 @@ def collect_readline(info_add):
|
||||
info_add('readline.library', 'GNU readline')
|
||||
|
||||
|
||||
def collect_gdb(info_add):
|
||||
def run_command(cmd, check=True, **kwargs):
|
||||
import subprocess
|
||||
from test.support import has_subprocess_support
|
||||
|
||||
if not has_subprocess_support:
|
||||
# subprocess is not supported by the current platform
|
||||
return ''
|
||||
|
||||
timeout = COMMAND_TIMEOUT
|
||||
|
||||
cmd_str = ' '.join(cmd)
|
||||
try:
|
||||
proc = subprocess.Popen(["gdb", "-nx", "--version"],
|
||||
proc = subprocess.Popen(cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
version = proc.communicate()[0]
|
||||
if proc.returncode:
|
||||
# ignore gdb failure: test_gdb will log the error
|
||||
return
|
||||
except OSError:
|
||||
return
|
||||
stderr=subprocess.DEVNULL,
|
||||
text=True,
|
||||
**kwargs)
|
||||
with proc:
|
||||
try:
|
||||
stdout = proc.communicate(timeout=timeout)[0]
|
||||
except:
|
||||
proc.kill()
|
||||
proc.communicate()
|
||||
raise
|
||||
|
||||
# Only keep the first line
|
||||
version = version.splitlines()[0]
|
||||
info_add('gdb_version', version)
|
||||
if check and proc.returncode:
|
||||
print(f"Command {cmd_str} failed with exit code {proc.returncode}")
|
||||
return ''
|
||||
|
||||
# Strip trailing spaces and newlines
|
||||
stdout = stdout.rstrip()
|
||||
return stdout
|
||||
except FileNotFoundError:
|
||||
return ''
|
||||
except OSError as exc:
|
||||
print(f"Command {cmd_str} failed with: {exc!r}")
|
||||
return ''
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f"Command {cmd_str}: timeout!")
|
||||
return ''
|
||||
|
||||
|
||||
def collect_gdb(info_add):
|
||||
version = run_command(["gdb", "-nx", "--version"])
|
||||
if version:
|
||||
# Only keep the first line
|
||||
info_add('gdb_version', first_line(version))
|
||||
|
||||
|
||||
def collect_tkinter(info_add):
|
||||
@@ -507,6 +570,7 @@ def collect_sysconfig(info_add):
|
||||
|
||||
for name in (
|
||||
'ABIFLAGS',
|
||||
'ALT_SOABI',
|
||||
'ANDROID_API_LEVEL',
|
||||
'CC',
|
||||
'CCSHARED',
|
||||
@@ -522,15 +586,19 @@ def collect_sysconfig(info_add):
|
||||
'PY_CFLAGS',
|
||||
'PY_CFLAGS_NODIST',
|
||||
'PY_CORE_LDFLAGS',
|
||||
'PY_CORE_EXE_LDFLAGS',
|
||||
'PY_LDFLAGS',
|
||||
'PY_LDFLAGS_NODIST',
|
||||
'PY_STDMODULE_CFLAGS',
|
||||
'Py_DEBUG',
|
||||
'Py_ENABLE_SHARED',
|
||||
'Py_GIL_DISABLED',
|
||||
'Py_REMOTE_DEBUG',
|
||||
'SHELL',
|
||||
'SOABI',
|
||||
'SOABI_PLATFORM',
|
||||
'TEST_MODULES',
|
||||
'VAPTH',
|
||||
'abs_builddir',
|
||||
'abs_srcdir',
|
||||
'prefix',
|
||||
@@ -554,7 +622,6 @@ def collect_sysconfig(info_add):
|
||||
for name in (
|
||||
'WITH_DOC_STRINGS',
|
||||
'WITH_DTRACE',
|
||||
'WITH_FREELISTS',
|
||||
'WITH_MIMALLOC',
|
||||
'WITH_PYMALLOC',
|
||||
'WITH_VALGRIND',
|
||||
@@ -652,10 +719,20 @@ def collect_zlib(info_add):
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
attributes = ('ZLIB_VERSION', 'ZLIB_RUNTIME_VERSION')
|
||||
attributes = ('ZLIB_VERSION', 'ZLIB_RUNTIME_VERSION', 'ZLIBNG_VERSION')
|
||||
copy_attributes(info_add, zlib, 'zlib.%s', attributes)
|
||||
|
||||
|
||||
def collect_zstd(info_add):
|
||||
try:
|
||||
import _zstd
|
||||
except ImportError:
|
||||
return
|
||||
|
||||
attributes = ('zstd_version',)
|
||||
copy_attributes(info_add, _zstd, 'zstd.%s', attributes)
|
||||
|
||||
|
||||
def collect_expat(info_add):
|
||||
try:
|
||||
from xml.parsers import expat
|
||||
@@ -685,7 +762,6 @@ def collect_testcapi(info_add):
|
||||
for name in (
|
||||
'LONG_MAX', # always 32-bit on Windows, 64-bit on 64-bit Unix
|
||||
'PY_SSIZE_T_MAX',
|
||||
'Py_C_RECURSION_LIMIT',
|
||||
'SIZEOF_TIME_T', # 32-bit or 64-bit depending on the platform
|
||||
'SIZEOF_WCHAR_T', # 16-bit or 32-bit depending on the platform
|
||||
):
|
||||
@@ -734,6 +810,10 @@ def collect_test_socket(info_add):
|
||||
if name.startswith('HAVE_')]
|
||||
copy_attributes(info_add, test_socket, 'test_socket.%s', attributes)
|
||||
|
||||
# Get IOCTL_VM_SOCKETS_GET_LOCAL_CID of /dev/vsock
|
||||
cid = test_socket.get_cid()
|
||||
info_add('test_socket.get_cid', cid)
|
||||
|
||||
|
||||
def collect_support(info_add):
|
||||
try:
|
||||
@@ -751,6 +831,7 @@ def collect_support(info_add):
|
||||
'is_emscripten',
|
||||
'is_jython',
|
||||
'is_wasi',
|
||||
'is_wasm32',
|
||||
)
|
||||
copy_attributes(info_add, support, 'support.%s', attributes)
|
||||
|
||||
@@ -813,7 +894,6 @@ def collect_support_threading_helper(info_add):
|
||||
|
||||
|
||||
def collect_cc(info_add):
|
||||
import subprocess
|
||||
import sysconfig
|
||||
|
||||
CC = sysconfig.get_config_var('CC')
|
||||
@@ -826,23 +906,17 @@ def collect_cc(info_add):
|
||||
except ImportError:
|
||||
args = CC.split()
|
||||
args.append('--version')
|
||||
try:
|
||||
proc = subprocess.Popen(args,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True)
|
||||
except OSError:
|
||||
|
||||
stdout = run_command(args)
|
||||
if not stdout:
|
||||
# Cannot run the compiler, for example when Python has been
|
||||
# cross-compiled and installed on the target platform where the
|
||||
# compiler is missing.
|
||||
return
|
||||
|
||||
stdout = proc.communicate()[0]
|
||||
if proc.returncode:
|
||||
#
|
||||
# CC --version failed: ignore error
|
||||
return
|
||||
|
||||
text = stdout.splitlines()[0]
|
||||
text = first_line(stdout)
|
||||
text = normalize_text(text)
|
||||
info_add('CC.version', text)
|
||||
|
||||
@@ -875,8 +949,48 @@ def collect_subprocess(info_add):
|
||||
copy_attributes(info_add, subprocess, 'subprocess.%s', ('_USE_POSIX_SPAWN',))
|
||||
|
||||
|
||||
def winreg_query(path):
|
||||
try:
|
||||
import winreg
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
key, path = path.split('\\', 1)
|
||||
sub_key, value = path.rsplit('\\', 1)
|
||||
if key == "HKEY_LOCAL_MACHINE":
|
||||
key = winreg.HKEY_LOCAL_MACHINE
|
||||
else:
|
||||
raise ValueError(f"unknown key {key!r}")
|
||||
|
||||
try:
|
||||
access = winreg.KEY_READ | winreg.KEY_WOW64_64KEY
|
||||
with winreg.OpenKey(key, sub_key, access=access) as key_handle:
|
||||
result, _ = winreg.QueryValueEx(key_handle, value)
|
||||
return result
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
def wmi_query(query):
|
||||
try:
|
||||
import _wmi
|
||||
except ImportError:
|
||||
return {}
|
||||
|
||||
try:
|
||||
data = _wmi.exec_query(query)
|
||||
except OSError:
|
||||
return {}
|
||||
|
||||
dict_data = {}
|
||||
for item in data.split("\0"):
|
||||
key, _, value = item.partition("=")
|
||||
dict_data[key] = value
|
||||
return dict_data
|
||||
|
||||
|
||||
def collect_windows(info_add):
|
||||
if sys.platform != "win32":
|
||||
if not MS_WINDOWS:
|
||||
# Code specific to Windows
|
||||
return
|
||||
|
||||
@@ -909,71 +1023,39 @@ def collect_windows(info_add):
|
||||
|
||||
try:
|
||||
import _winapi
|
||||
dll_path = _winapi.GetModuleFileName(sys.dllhandle)
|
||||
info_add('windows.dll_path', dll_path)
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
|
||||
# windows.version_caption: "wmic os get Caption,Version /value" command
|
||||
import subprocess
|
||||
try:
|
||||
# When wmic.exe output is redirected to a pipe,
|
||||
# it uses the OEM code page
|
||||
proc = subprocess.Popen(["wmic", "os", "get", "Caption,Version", "/value"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
encoding="oem",
|
||||
text=True)
|
||||
output, stderr = proc.communicate()
|
||||
if proc.returncode:
|
||||
output = ""
|
||||
except OSError:
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
for line in output.splitlines():
|
||||
line = line.strip()
|
||||
if line.startswith('Caption='):
|
||||
line = line.removeprefix('Caption=').strip()
|
||||
if line:
|
||||
info_add('windows.version_caption', line)
|
||||
elif line.startswith('Version='):
|
||||
line = line.removeprefix('Version=').strip()
|
||||
if line:
|
||||
info_add('windows.version', line)
|
||||
try:
|
||||
dll_path = _winapi.GetModuleFileName(sys.dllhandle)
|
||||
info_add('windows.dll_path', dll_path)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
call_func(info_add, 'windows.ansi_code_page', _winapi, 'GetACP')
|
||||
call_func(info_add, 'windows.oem_code_page', _winapi, 'GetOEMCP')
|
||||
|
||||
# Get operating system caption and version using WMI
|
||||
data = wmi_query("SELECT Caption, Version FROM Win32_OperatingSystem")
|
||||
caption = data.get('Caption', '')
|
||||
if caption:
|
||||
info_add('windows.version_caption', caption)
|
||||
version = data.get('Version', '')
|
||||
if version:
|
||||
info_add('windows.version', version)
|
||||
|
||||
# windows.ver: "ver" command
|
||||
try:
|
||||
proc = subprocess.Popen(["ver"], shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True)
|
||||
output = proc.communicate()[0]
|
||||
if proc.returncode == 0xc0000142:
|
||||
return
|
||||
if proc.returncode:
|
||||
output = ""
|
||||
except OSError:
|
||||
return
|
||||
else:
|
||||
output = output.strip()
|
||||
line = output.splitlines()[0]
|
||||
if line:
|
||||
info_add('windows.ver', line)
|
||||
output = run_command(["ver"], shell=True)
|
||||
# "ver" output starts with an empty line: remove it
|
||||
output = output.strip()
|
||||
if output:
|
||||
info_add('windows.ver', first_line(output))
|
||||
|
||||
# windows.developer_mode: get AllowDevelopmentWithoutDevLicense registry
|
||||
import winreg
|
||||
try:
|
||||
key = winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock")
|
||||
subkey = "AllowDevelopmentWithoutDevLicense"
|
||||
try:
|
||||
value, value_type = winreg.QueryValueEx(key, subkey)
|
||||
finally:
|
||||
winreg.CloseKey(key)
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
value = winreg_query(r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows"
|
||||
r"\CurrentVersion\AppModelUnlock"
|
||||
r"\AllowDevelopmentWithoutDevLicense")
|
||||
if value is not None:
|
||||
info_add('windows.developer_mode', "enabled" if value else "disabled")
|
||||
|
||||
|
||||
@@ -986,14 +1068,9 @@ def collect_fips(info_add):
|
||||
if _hashlib is not None:
|
||||
call_func(info_add, 'fips.openssl_fips_mode', _hashlib, 'get_fips_mode')
|
||||
|
||||
try:
|
||||
with open("/proc/sys/crypto/fips_enabled", encoding="utf-8") as fp:
|
||||
line = fp.readline().rstrip()
|
||||
|
||||
if line:
|
||||
info_add('fips.linux_crypto_fips_enabled', line)
|
||||
except OSError:
|
||||
pass
|
||||
fips_enabled = read_first_line("/proc/sys/crypto/fips_enabled")
|
||||
if fips_enabled:
|
||||
info_add('fips.linux_crypto_fips_enabled', fips_enabled)
|
||||
|
||||
|
||||
def collect_tempfile(info_add):
|
||||
@@ -1011,6 +1088,242 @@ def collect_libregrtest_utils(info_add):
|
||||
info_add('libregrtests.build_info', ' '.join(utils.get_build_info()))
|
||||
|
||||
|
||||
def uptime_boottime():
|
||||
# Use CLOCK_BOOTTIME
|
||||
import time
|
||||
try:
|
||||
return time.clock_gettime(time.CLOCK_BOOTTIME)
|
||||
except (AttributeError, OSError):
|
||||
return None
|
||||
|
||||
|
||||
def uptime_linux():
|
||||
# Parse the first member of /proc/uptime
|
||||
line = read_first_line("/proc/uptime")
|
||||
if not line:
|
||||
return
|
||||
try:
|
||||
parts = line.split()
|
||||
if not parts:
|
||||
return
|
||||
return float(parts[0])
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
|
||||
def uptime_bsd():
|
||||
# Get sysctlbyname("kern.boottime")
|
||||
try:
|
||||
import _testcapi
|
||||
except ImportError:
|
||||
return None
|
||||
try:
|
||||
return _testcapi.uptime_bsd()
|
||||
except (AttributeError, OSError):
|
||||
return None
|
||||
|
||||
|
||||
def uptime_windows():
|
||||
try:
|
||||
import _winapi
|
||||
except ImportError:
|
||||
return None
|
||||
else:
|
||||
return _winapi.GetTickCount64() / 1000.
|
||||
|
||||
|
||||
def get_uptime():
|
||||
for func in (uptime_boottime, uptime_linux, uptime_bsd, uptime_windows):
|
||||
uptime = func()
|
||||
if uptime is not None:
|
||||
return uptime
|
||||
return None
|
||||
|
||||
|
||||
def get_machine_id():
|
||||
if MS_WINDOWS:
|
||||
machine_guid = winreg_query(r"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft"
|
||||
r"\Cryptography\MachineGuid")
|
||||
if machine_guid:
|
||||
return machine_guid
|
||||
|
||||
for filename in (
|
||||
# https://www.freedesktop.org/software/systemd/man/latest/machine-id.html
|
||||
"/etc/machine-id",
|
||||
# BSD
|
||||
"/etc/hostid",
|
||||
):
|
||||
machine_id = read_first_line(filename)
|
||||
if machine_id:
|
||||
return machine_id
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def detect_virt_windows(info_add):
|
||||
# On Windows, use WMI to detect the virtualization.
|
||||
#
|
||||
# Microsoft Hyper-V:
|
||||
# - Win32_Bios.Version = 'VRTUAL - 12001807'
|
||||
# - Win32_Bios.Manufacturer = 'American Megatrends Inc.'
|
||||
# - Win32_ComputerSystem.Model = 'Virtual Machine'
|
||||
# - Win32_ComputerSystem.Manufacturer = 'Microsoft Corporation'
|
||||
#
|
||||
# VMware:
|
||||
# - Win32_ComputerSystem.Model = 'VMware'
|
||||
# - Win32_ComputerSystem.Manufacturer = 'VMWare' (uppercase W in Ware)
|
||||
# - Win32_Bios.SerialNumber starts with 'VMware-'
|
||||
#
|
||||
# QEMU:
|
||||
# - Win32_ComputerSystem.Manufacturer = 'QEMU'
|
||||
# - Win32_ComputerSystem.Model = 'Standard PC (Q35 + ICH9, 2009)'
|
||||
# - Win32_Bios.Version = 'BOCHS - 1'
|
||||
# - Win32_Bios.Manufacturer = 'EDK II'
|
||||
#
|
||||
# Parallels:
|
||||
# - Win32_Bios.Version = 'PARALLELS'
|
||||
#
|
||||
# VirtualBox:
|
||||
# - Win32_Bios.Version = 'VBOX'
|
||||
# - Win32_ComputerSystem.Model = 'VirtualBox'
|
||||
# - Win32_ComputerSystem.Manufacturer = 'innotek GmbH'
|
||||
#
|
||||
# Amazon EC2:
|
||||
# - Win32_Bios.Version = 'AMAZON - 1'
|
||||
# - Win32_Bios.Manufacturer = 'Amazon EC2'
|
||||
# - Win32_ComputerSystem.Model = 'm7i.4xlarge'
|
||||
# - Win32_ComputerSystem.Manufacturer = 'Amazon EC2'
|
||||
|
||||
KNOWN_VIRT = (
|
||||
'Amazon EC2',
|
||||
'QEMU',
|
||||
'VMware',
|
||||
'VirtualBox',
|
||||
'Xen',
|
||||
'oVirt',
|
||||
)
|
||||
KNOWN_BIOS_VERSIONS = {
|
||||
'PARALLELS': 'Parallels',
|
||||
'VBOX': 'VirtualBox',
|
||||
}
|
||||
|
||||
computer = wmi_query('SELECT Model, Manufacturer FROM Win32_ComputerSystem')
|
||||
computer_model = computer.get('Model', '')
|
||||
computer_manufacturer = computer.get('Manufacturer', '')
|
||||
if computer_manufacturer == 'Amazon EC2':
|
||||
# Log the VM model (ex: 'm7i.4xlarge')
|
||||
info_add('system.computer.model', computer_model)
|
||||
return computer_manufacturer
|
||||
if computer_model in KNOWN_VIRT:
|
||||
return computer_model
|
||||
if computer_manufacturer in KNOWN_VIRT:
|
||||
return computer_manufacturer
|
||||
|
||||
bios = wmi_query('SELECT Version, Manufacturer FROM Win32_Bios')
|
||||
|
||||
bios_version = bios.get('Version', '')
|
||||
if bios_version in KNOWN_VIRT:
|
||||
return bios_version
|
||||
if (bios_version.startswith('VRTUAL - ')
|
||||
and computer_manufacturer == 'Microsoft Corporation'):
|
||||
return 'Microsoft Hyper-V'
|
||||
try:
|
||||
return KNOWN_BIOS_VERSIONS[bios_version]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
bios_manufacturer = bios.get('Manufacturer', '')
|
||||
if bios_manufacturer in KNOWN_VIRT:
|
||||
return bios_manufacturer
|
||||
|
||||
# Log the values to update the code if a new VM is discovered
|
||||
if computer_model:
|
||||
info_add('system.computer.model', computer_model)
|
||||
if computer_manufacturer:
|
||||
info_add('system.computer.manufacturer', computer_manufacturer)
|
||||
if bios_version:
|
||||
info_add('system.bios.version', bios_version)
|
||||
if bios_manufacturer:
|
||||
info_add('system.bios.manufacturer', bios_manufacturer)
|
||||
|
||||
|
||||
def detect_virt(info_add):
|
||||
if MS_WINDOWS:
|
||||
return detect_virt_windows(info_add)
|
||||
|
||||
# Run systemd-detect-virt command
|
||||
virt = run_command(["systemd-detect-virt"], check=False)
|
||||
if virt and virt != "none":
|
||||
return virt
|
||||
|
||||
# Check if the process in running in a container
|
||||
import os.path
|
||||
if os.path.exists('/.dockerenv'):
|
||||
return 'docker'
|
||||
if os.path.exists('/run/.containerenv'):
|
||||
return 'podman'
|
||||
|
||||
container = read_first_line('/run/systemd/container')
|
||||
if container:
|
||||
return container
|
||||
|
||||
if APPLE:
|
||||
hv_vmm_present = run_command(['sysctl', '-n', 'kern.hv_vmm_present'])
|
||||
if hv_vmm_present == '1':
|
||||
return 'run in a VM (kern.hv_vmm_present is 1)'
|
||||
|
||||
# Other ways to check if running in a container:
|
||||
# * Parse /proc/1/mounts or /proc/1/mountinfo (check "/" filesystem).
|
||||
# * Parse /proc/1/cgroup.
|
||||
# * Parse the first line of /proc/1/sched (check process name is different
|
||||
# than "init" and "systemd").
|
||||
# * Check / inode.
|
||||
# * On systems using SELinux (Fedora/CentOS/RHEL), check for "container_t"
|
||||
# label, for example of /proc/1/attr/current.
|
||||
# * Check for "container" variable in /proc/1/environ
|
||||
# (only root can read this file).
|
||||
# * Check for "container" environment variable.
|
||||
# * Set a specific env var when creating the container image.
|
||||
# * Run virt-what, need to install the script, and must be run as root.
|
||||
# * Check for "GITHUB_ACTIONS" environmant variable (GitHub Action).
|
||||
|
||||
|
||||
def collect_system(info_add):
|
||||
boot_id = read_first_line("/proc/sys/kernel/random/boot_id")
|
||||
if boot_id:
|
||||
info_add('system.boot_id', boot_id)
|
||||
|
||||
machine_id = get_machine_id()
|
||||
if machine_id:
|
||||
info_add('system.machine_id', machine_id)
|
||||
|
||||
uptime = get_uptime()
|
||||
if uptime is not None:
|
||||
# truncate microseconds
|
||||
uptime = int(uptime)
|
||||
try:
|
||||
import datetime
|
||||
uptime = str(datetime.timedelta(seconds=uptime))
|
||||
except ImportError:
|
||||
uptime = f'{uptime} sec'
|
||||
info_add('system.uptime', uptime)
|
||||
|
||||
virt = detect_virt(info_add)
|
||||
if virt:
|
||||
info_add('system.virt', virt)
|
||||
|
||||
if APPLE:
|
||||
hardware = run_command(['sysctl', '-n', 'hw.model'])
|
||||
if hardware:
|
||||
info_add('system.hardware', hardware)
|
||||
|
||||
|
||||
def collect_importlib(info_add):
|
||||
import importlib.machinery
|
||||
info_add('importlib.extension_suffixes',
|
||||
importlib.machinery.EXTENSION_SUFFIXES)
|
||||
|
||||
|
||||
def collect_info(info):
|
||||
error = False
|
||||
info_add = info.add
|
||||
@@ -1050,7 +1363,10 @@ def collect_info(info):
|
||||
collect_tkinter,
|
||||
collect_windows,
|
||||
collect_zlib,
|
||||
collect_zstd,
|
||||
collect_libregrtest_utils,
|
||||
collect_system,
|
||||
collect_importlib,
|
||||
|
||||
# Collecting from tests should be last as they have side effects.
|
||||
collect_test_socket,
|
||||
@@ -1073,9 +1389,9 @@ def collect_info(info):
|
||||
|
||||
|
||||
def dump_info(info, file=None):
|
||||
title = "Python debug information"
|
||||
title = "Python build information"
|
||||
print(title)
|
||||
print("=" * len(title))
|
||||
print("#" * len(title))
|
||||
print()
|
||||
|
||||
infos = info.get_infos()
|
||||
|
||||
Vendored
+65
-1
@@ -12,6 +12,7 @@ from itertools import product, chain, combinations
|
||||
import string
|
||||
import sys
|
||||
from test import support
|
||||
from test.support.import_helper import import_fresh_module
|
||||
import types
|
||||
import unittest
|
||||
|
||||
@@ -26,7 +27,7 @@ from collections.abc import Sized, Container, Callable, Collection
|
||||
from collections.abc import Set, MutableSet
|
||||
from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView
|
||||
from collections.abc import Sequence, MutableSequence
|
||||
from collections.abc import ByteString, Buffer
|
||||
from collections.abc import Buffer
|
||||
|
||||
|
||||
class TestUserObjects(unittest.TestCase):
|
||||
@@ -1935,6 +1936,14 @@ class TestCollectionABCs(ABCTestCase):
|
||||
nativeseq, seqseq, (letter, start, stop))
|
||||
|
||||
def test_ByteString(self):
|
||||
previous_sys_modules = sys.modules.copy()
|
||||
self.addCleanup(sys.modules.update, previous_sys_modules)
|
||||
|
||||
for module in "collections", "_collections_abc", "collections.abc":
|
||||
sys.modules.pop(module, None)
|
||||
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
from collections.abc import ByteString
|
||||
for sample in [bytes, bytearray]:
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
self.assertIsInstance(sample(), ByteString)
|
||||
@@ -1956,6 +1965,14 @@ class TestCollectionABCs(ABCTestCase):
|
||||
# No metaclass conflict
|
||||
class Z(ByteString, Awaitable): pass
|
||||
|
||||
def test_ByteString_attribute_access(self):
|
||||
collections_abc = import_fresh_module(
|
||||
"collections.abc",
|
||||
fresh=("collections", "_collections_abc")
|
||||
)
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
collections_abc.ByteString
|
||||
|
||||
def test_Buffer(self):
|
||||
for sample in [bytes, bytearray, memoryview]:
|
||||
self.assertIsInstance(sample(b"x"), Buffer)
|
||||
@@ -2175,6 +2192,7 @@ class TestCounter(unittest.TestCase):
|
||||
self.assertTrue(correctly_ordered(p - q))
|
||||
self.assertTrue(correctly_ordered(p | q))
|
||||
self.assertTrue(correctly_ordered(p & q))
|
||||
self.assertTrue(correctly_ordered(p ^ q))
|
||||
|
||||
p, q = Counter(ps), Counter(qs)
|
||||
p += q
|
||||
@@ -2192,6 +2210,10 @@ class TestCounter(unittest.TestCase):
|
||||
p &= q
|
||||
self.assertTrue(correctly_ordered(p))
|
||||
|
||||
p, q = Counter(ps), Counter(qs)
|
||||
p ^= q
|
||||
self.assertTrue(correctly_ordered(p))
|
||||
|
||||
p, q = Counter(ps), Counter(qs)
|
||||
p.update(q)
|
||||
self.assertTrue(correctly_ordered(p))
|
||||
@@ -2274,6 +2296,7 @@ class TestCounter(unittest.TestCase):
|
||||
(Counter.__sub__, lambda x, y: max(0, x-y)),
|
||||
(Counter.__or__, lambda x, y: max(0,x,y)),
|
||||
(Counter.__and__, lambda x, y: max(0, min(x,y))),
|
||||
(Counter.__xor__, lambda x, y: max(0, max(x,y) - min(x,y))),
|
||||
]:
|
||||
result = counterop(p, q)
|
||||
for x in elements:
|
||||
@@ -2291,6 +2314,7 @@ class TestCounter(unittest.TestCase):
|
||||
(Counter.__sub__, set.__sub__),
|
||||
(Counter.__or__, set.__or__),
|
||||
(Counter.__and__, set.__and__),
|
||||
(Counter.__xor__, set.__xor__),
|
||||
]:
|
||||
counter_result = counterop(p, q)
|
||||
set_result = setop(set(p.elements()), set(q.elements()))
|
||||
@@ -2309,6 +2333,7 @@ class TestCounter(unittest.TestCase):
|
||||
(Counter.__isub__, Counter.__sub__),
|
||||
(Counter.__ior__, Counter.__or__),
|
||||
(Counter.__iand__, Counter.__and__),
|
||||
(Counter.__ixor__, Counter.__xor__),
|
||||
]:
|
||||
c = p.copy()
|
||||
c_id = id(c)
|
||||
@@ -2384,6 +2409,7 @@ class TestCounter(unittest.TestCase):
|
||||
self.assertEqual(set(cp - cq), sp - sq)
|
||||
self.assertEqual(set(cp | cq), sp | sq)
|
||||
self.assertEqual(set(cp & cq), sp & sq)
|
||||
self.assertEqual(set(cp ^ cq), sp ^ sq)
|
||||
self.assertEqual(cp == cq, sp == sq)
|
||||
self.assertEqual(cp != cq, sp != sq)
|
||||
self.assertEqual(cp <= cq, sp <= sq)
|
||||
@@ -2397,6 +2423,8 @@ class TestCounter(unittest.TestCase):
|
||||
|
||||
def test_le(self):
|
||||
self.assertTrue(Counter(a=3, b=2, c=0) <= Counter('ababa'))
|
||||
self.assertTrue(Counter() <= Counter(c=1))
|
||||
self.assertFalse(Counter() <= Counter(c=-1))
|
||||
self.assertFalse(Counter(a=3, b=2) <= Counter('babab'))
|
||||
|
||||
def test_lt(self):
|
||||
@@ -2405,12 +2433,48 @@ class TestCounter(unittest.TestCase):
|
||||
|
||||
def test_ge(self):
|
||||
self.assertTrue(Counter(a=2, b=1, c=0) >= Counter('aab'))
|
||||
self.assertTrue(Counter() >= Counter(c=-1))
|
||||
self.assertFalse(Counter() >= Counter(c=1))
|
||||
self.assertFalse(Counter(a=3, b=2, c=0) >= Counter('aabd'))
|
||||
|
||||
def test_gt(self):
|
||||
self.assertTrue(Counter(a=3, b=2, c=0) > Counter('aab'))
|
||||
self.assertFalse(Counter(a=2, b=1, c=0) > Counter('aab'))
|
||||
|
||||
def test_symmetric_difference(self):
|
||||
population = (-4, -3, -2, -1, 0, 1, 2, 3, 4)
|
||||
|
||||
for a, b1, b2, c in product(population, repeat=4):
|
||||
p = Counter(a=a, b=b1)
|
||||
q = Counter(b=b2, c=c)
|
||||
r = p ^ q
|
||||
|
||||
# Elementwise invariants
|
||||
for k in ('a', 'b', 'c'):
|
||||
self.assertEqual(r[k], max(p[k], q[k]) - min(p[k], q[k]))
|
||||
self.assertEqual(r[k], abs(p[k] - q[k]))
|
||||
|
||||
# Invariant for all positive, negative, and zero counts
|
||||
self.assertEqual(r, (p - q) | (q - p))
|
||||
|
||||
# Invariant for non-negative counts
|
||||
if a >= 0 and b1 >= 0 and b2 >= 0 and c >= 0:
|
||||
self.assertEqual(r, (p | q) - (p & q))
|
||||
|
||||
# Zeros and negatives eliminated
|
||||
self.assertTrue(all(value > 0 for value in r.values()))
|
||||
|
||||
# Output preserves input order: p first and then q
|
||||
keys = list(p) + list(q)
|
||||
indices = [keys.index(k) for k in r]
|
||||
self.assertEqual(indices, sorted(indices))
|
||||
|
||||
# Inplace operation matches binary operation
|
||||
pp = Counter(p)
|
||||
qq = Counter(q)
|
||||
pp ^= qq
|
||||
self.assertEqual(pp, r)
|
||||
|
||||
|
||||
def load_tests(loader, tests, pattern):
|
||||
tests.addTest(doctest.DocTestSuite(collections))
|
||||
|
||||
Vendored
+217
@@ -680,6 +680,154 @@ class TestContextDecorator(unittest.TestCase):
|
||||
self.assertEqual(state, [1, 'something else', 999])
|
||||
|
||||
|
||||
def test_contextmanager_decorate_generator_function(self):
|
||||
@contextmanager
|
||||
def woohoo(y):
|
||||
state.append(y)
|
||||
yield
|
||||
state.append(999)
|
||||
|
||||
state = []
|
||||
@woohoo(1)
|
||||
def test(x):
|
||||
self.assertEqual(state, [1])
|
||||
state.append(x)
|
||||
yield
|
||||
state.append("second item")
|
||||
return "result"
|
||||
|
||||
gen = test("something")
|
||||
for _ in gen:
|
||||
self.assertEqual(state, [1, "something"])
|
||||
self.assertEqual(state, [1, "something", "second item", 999])
|
||||
|
||||
# The wrapped generator's return value is preserved.
|
||||
state = []
|
||||
gen = test("something")
|
||||
with self.assertRaises(StopIteration) as cm:
|
||||
while True:
|
||||
next(gen)
|
||||
self.assertEqual(cm.exception.value, "result")
|
||||
|
||||
|
||||
def test_contextmanager_decorate_generator_function_exception(self):
|
||||
@contextmanager
|
||||
def woohoo():
|
||||
state.append("enter")
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
state.append("exit")
|
||||
|
||||
state = []
|
||||
@woohoo()
|
||||
def test():
|
||||
state.append("body")
|
||||
yield
|
||||
raise ZeroDivisionError
|
||||
|
||||
with self.assertRaises(ZeroDivisionError):
|
||||
for _ in test():
|
||||
pass
|
||||
self.assertEqual(state, ["enter", "body", "exit"])
|
||||
|
||||
|
||||
def test_contextmanager_decorate_generator_function_early_stop(self):
|
||||
@contextmanager
|
||||
def woohoo():
|
||||
state.append("enter")
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
state.append("exit")
|
||||
|
||||
state = []
|
||||
@woohoo()
|
||||
def test():
|
||||
try:
|
||||
yield 1
|
||||
yield 2
|
||||
finally:
|
||||
state.append("inner closed")
|
||||
|
||||
gen = test()
|
||||
self.assertEqual(next(gen), 1)
|
||||
gen.close()
|
||||
# The inner generator is closed before the context manager exits.
|
||||
self.assertEqual(state, ["enter", "inner closed", "exit"])
|
||||
|
||||
|
||||
def test_contextmanager_decorate_generator_function_send_throw(self):
|
||||
@contextmanager
|
||||
def woohoo():
|
||||
yield
|
||||
|
||||
@woohoo()
|
||||
def test():
|
||||
received = yield "first"
|
||||
state.append(("received", received))
|
||||
try:
|
||||
yield "second"
|
||||
except ValueError as exc:
|
||||
state.append(("caught", type(exc)))
|
||||
yield "after throw"
|
||||
|
||||
# .send() and .throw() are forwarded to the wrapped generator.
|
||||
state = []
|
||||
gen = test()
|
||||
self.assertEqual(next(gen), "first")
|
||||
self.assertEqual(gen.send("VALUE"), "second")
|
||||
self.assertEqual(gen.throw(ValueError), "after throw")
|
||||
gen.close()
|
||||
self.assertEqual(
|
||||
state, [("received", "VALUE"), ("caught", ValueError)]
|
||||
)
|
||||
|
||||
|
||||
def test_contextmanager_decorate_coroutine_function(self):
|
||||
@contextmanager
|
||||
def woohoo(y):
|
||||
state.append(y)
|
||||
yield
|
||||
state.append(999)
|
||||
|
||||
state = []
|
||||
@woohoo(1)
|
||||
async def test(x):
|
||||
self.assertEqual(state, [1])
|
||||
state.append(x)
|
||||
|
||||
coro = test("something")
|
||||
with self.assertRaises(StopIteration):
|
||||
coro.send(None)
|
||||
|
||||
self.assertEqual(state, [1, "something", 999])
|
||||
|
||||
|
||||
def test_contextmanager_decorate_asyncgen_function(self):
|
||||
@contextmanager
|
||||
def woohoo(y):
|
||||
state.append(y)
|
||||
yield
|
||||
state.append(999)
|
||||
|
||||
state = []
|
||||
@woohoo(1)
|
||||
async def test(x):
|
||||
self.assertEqual(state, [1])
|
||||
state.append(x)
|
||||
yield
|
||||
state.append("second item")
|
||||
|
||||
agen = test("something")
|
||||
with self.assertRaises(StopIteration):
|
||||
agen.asend(None).send(None)
|
||||
with self.assertRaises(StopAsyncIteration):
|
||||
agen.asend(None).send(None)
|
||||
|
||||
self.assertEqual(state, [1, "something", "second item", 999])
|
||||
|
||||
|
||||
class TestBaseExitStack:
|
||||
exit_stack = None
|
||||
|
||||
@@ -788,6 +936,75 @@ class TestBaseExitStack:
|
||||
result.append(2)
|
||||
self.assertEqual(result, [1, 2, 3, 4])
|
||||
|
||||
def test_enter_context_classmethod(self):
|
||||
class TestCM:
|
||||
@classmethod
|
||||
def __enter__(cls):
|
||||
result.append(('enter', cls))
|
||||
@classmethod
|
||||
def __exit__(cls, *exc_details):
|
||||
result.append(('exit', cls, *exc_details))
|
||||
|
||||
cm = TestCM()
|
||||
result = []
|
||||
with self.exit_stack() as stack:
|
||||
stack.enter_context(cm)
|
||||
self.assertEqual(result, [('enter', TestCM)])
|
||||
self.assertEqual(result, [('enter', TestCM),
|
||||
('exit', TestCM, None, None, None)])
|
||||
|
||||
result = []
|
||||
with self.exit_stack() as stack:
|
||||
stack.push(cm)
|
||||
self.assertEqual(result, [])
|
||||
self.assertEqual(result, [('exit', TestCM, None, None, None)])
|
||||
|
||||
def test_enter_context_staticmethod(self):
|
||||
class TestCM:
|
||||
@staticmethod
|
||||
def __enter__():
|
||||
result.append('enter')
|
||||
@staticmethod
|
||||
def __exit__(*exc_details):
|
||||
result.append(('exit', *exc_details))
|
||||
|
||||
cm = TestCM()
|
||||
result = []
|
||||
with self.exit_stack() as stack:
|
||||
stack.enter_context(cm)
|
||||
self.assertEqual(result, ['enter'])
|
||||
self.assertEqual(result, ['enter', ('exit', None, None, None)])
|
||||
|
||||
result = []
|
||||
with self.exit_stack() as stack:
|
||||
stack.push(cm)
|
||||
self.assertEqual(result, [])
|
||||
self.assertEqual(result, [('exit', None, None, None)])
|
||||
|
||||
def test_enter_context_slots(self):
|
||||
class TestCM:
|
||||
__slots__ = ('__enter__', '__exit__')
|
||||
def __init__(self):
|
||||
def enter():
|
||||
result.append('enter')
|
||||
def exit(*exc_details):
|
||||
result.append(('exit', *exc_details))
|
||||
self.__enter__ = enter
|
||||
self.__exit__ = exit
|
||||
|
||||
cm = TestCM()
|
||||
result = []
|
||||
with self.exit_stack() as stack:
|
||||
stack.enter_context(cm)
|
||||
self.assertEqual(result, ['enter'])
|
||||
self.assertEqual(result, ['enter', ('exit', None, None, None)])
|
||||
|
||||
result = []
|
||||
with self.exit_stack() as stack:
|
||||
stack.push(cm)
|
||||
self.assertEqual(result, [])
|
||||
self.assertEqual(result, [('exit', None, None, None)])
|
||||
|
||||
def test_enter_context_errors(self):
|
||||
class LacksEnterAndExit:
|
||||
pass
|
||||
|
||||
Vendored
+210
@@ -402,6 +402,144 @@ class AsyncContextManagerTestCase(unittest.TestCase):
|
||||
await test()
|
||||
self.assertFalse(entered)
|
||||
|
||||
@_async_test
|
||||
async def test_decorator_decorate_sync_function(self):
|
||||
@asynccontextmanager
|
||||
async def context():
|
||||
state.append(1)
|
||||
yield
|
||||
state.append(999)
|
||||
|
||||
state = []
|
||||
@context()
|
||||
def test(x):
|
||||
self.assertEqual(state, [1])
|
||||
state.append(x)
|
||||
|
||||
await test("something")
|
||||
self.assertEqual(state, [1, "something", 999])
|
||||
|
||||
@_async_test
|
||||
async def test_decorator_decorate_generator_function(self):
|
||||
@asynccontextmanager
|
||||
async def context():
|
||||
state.append(1)
|
||||
yield
|
||||
state.append(999)
|
||||
|
||||
state = []
|
||||
@context()
|
||||
def test(x):
|
||||
self.assertEqual(state, [1])
|
||||
state.append(x)
|
||||
yield
|
||||
state.append("second item")
|
||||
|
||||
async for _ in test("something"):
|
||||
self.assertEqual(state, [1, "something"])
|
||||
self.assertEqual(state, [1, "something", "second item", 999])
|
||||
|
||||
@_async_test
|
||||
async def test_decorator_decorate_asyncgen_function(self):
|
||||
@asynccontextmanager
|
||||
async def context():
|
||||
state.append(1)
|
||||
yield
|
||||
state.append(999)
|
||||
|
||||
state = []
|
||||
@context()
|
||||
async def test(x):
|
||||
self.assertEqual(state, [1])
|
||||
state.append(x)
|
||||
yield
|
||||
state.append("second item")
|
||||
|
||||
async for _ in test("something"):
|
||||
self.assertEqual(state, [1, "something"])
|
||||
self.assertEqual(state, [1, "something", "second item", 999])
|
||||
|
||||
@_async_test
|
||||
async def test_decorator_decorate_asyncgen_function_exception(self):
|
||||
@asynccontextmanager
|
||||
async def context():
|
||||
state.append("enter")
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
state.append("exit")
|
||||
|
||||
state = []
|
||||
@context()
|
||||
async def test():
|
||||
state.append("body")
|
||||
yield
|
||||
raise ZeroDivisionError
|
||||
|
||||
with self.assertRaises(ZeroDivisionError):
|
||||
async for _ in test():
|
||||
pass
|
||||
self.assertEqual(state, ["enter", "body", "exit"])
|
||||
|
||||
@_async_test
|
||||
async def test_decorator_decorate_asyncgen_function_early_stop(self):
|
||||
@asynccontextmanager
|
||||
async def context():
|
||||
state.append("enter")
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
state.append("exit")
|
||||
|
||||
state = []
|
||||
@context()
|
||||
async def test():
|
||||
try:
|
||||
yield 1
|
||||
yield 2
|
||||
finally:
|
||||
state.append("inner closed")
|
||||
|
||||
agen = test()
|
||||
async for value in agen:
|
||||
self.assertEqual(value, 1)
|
||||
break
|
||||
await agen.aclose()
|
||||
# The inner async generator is closed before the context
|
||||
# manager exits.
|
||||
self.assertEqual(state, ["enter", "inner closed", "exit"])
|
||||
|
||||
@_async_test
|
||||
async def test_decorator_decorate_asyncgen_function_asend_athrow(self):
|
||||
@asynccontextmanager
|
||||
async def context():
|
||||
yield
|
||||
|
||||
@context()
|
||||
async def test():
|
||||
try:
|
||||
received = yield "first"
|
||||
state.append(("received", received))
|
||||
yield "second"
|
||||
except ValueError:
|
||||
state.append("inner saw ValueError")
|
||||
raise
|
||||
finally:
|
||||
state.append("inner closed")
|
||||
|
||||
# asend() values and athrow() exceptions are not forwarded to the
|
||||
# wrapped generator (a documented limitation).
|
||||
state = []
|
||||
agen = test()
|
||||
self.assertEqual(await agen.__anext__(), "first")
|
||||
self.assertEqual(await agen.asend("VALUE"), "second")
|
||||
# The inner generator received None, not "VALUE".
|
||||
self.assertEqual(state, [("received", None)])
|
||||
with self.assertRaises(ValueError):
|
||||
await agen.athrow(ValueError)
|
||||
# The inner generator was closed, not thrown into.
|
||||
self.assertEqual(state, [("received", None), "inner closed"])
|
||||
|
||||
@_async_test
|
||||
async def test_decorator_with_exception(self):
|
||||
entered = False
|
||||
@@ -641,6 +779,78 @@ class TestAsyncExitStack(TestBaseExitStack, unittest.TestCase):
|
||||
|
||||
self.assertEqual(result, [1, 2, 3, 4])
|
||||
|
||||
@_async_test
|
||||
async def test_enter_async_context_classmethod(self):
|
||||
class TestCM:
|
||||
@classmethod
|
||||
async def __aenter__(cls):
|
||||
result.append(('enter', cls))
|
||||
@classmethod
|
||||
async def __aexit__(cls, *exc_details):
|
||||
result.append(('exit', cls, *exc_details))
|
||||
|
||||
cm = TestCM()
|
||||
result = []
|
||||
async with self.exit_stack() as stack:
|
||||
await stack.enter_async_context(cm)
|
||||
self.assertEqual(result, [('enter', TestCM)])
|
||||
self.assertEqual(result, [('enter', TestCM),
|
||||
('exit', TestCM, None, None, None)])
|
||||
|
||||
result = []
|
||||
async with self.exit_stack() as stack:
|
||||
stack.push_async_exit(cm)
|
||||
self.assertEqual(result, [])
|
||||
self.assertEqual(result, [('exit', TestCM, None, None, None)])
|
||||
|
||||
@_async_test
|
||||
async def test_enter_async_context_staticmethod(self):
|
||||
class TestCM:
|
||||
@staticmethod
|
||||
async def __aenter__():
|
||||
result.append('enter')
|
||||
@staticmethod
|
||||
async def __aexit__(*exc_details):
|
||||
result.append(('exit', *exc_details))
|
||||
|
||||
cm = TestCM()
|
||||
result = []
|
||||
async with self.exit_stack() as stack:
|
||||
await stack.enter_async_context(cm)
|
||||
self.assertEqual(result, ['enter'])
|
||||
self.assertEqual(result, ['enter', ('exit', None, None, None)])
|
||||
|
||||
result = []
|
||||
async with self.exit_stack() as stack:
|
||||
stack.push_async_exit(cm)
|
||||
self.assertEqual(result, [])
|
||||
self.assertEqual(result, [('exit', None, None, None)])
|
||||
|
||||
@_async_test
|
||||
async def test_enter_async_context_slots(self):
|
||||
class TestCM:
|
||||
__slots__ = ('__aenter__', '__aexit__')
|
||||
def __init__(self):
|
||||
async def enter():
|
||||
result.append('enter')
|
||||
async def exit(*exc_details):
|
||||
result.append(('exit', *exc_details))
|
||||
self.__aenter__ = enter
|
||||
self.__aexit__ = exit
|
||||
|
||||
cm = TestCM()
|
||||
result = []
|
||||
async with self.exit_stack() as stack:
|
||||
await stack.enter_async_context(cm)
|
||||
self.assertEqual(result, ['enter'])
|
||||
self.assertEqual(result, ['enter', ('exit', None, None, None)])
|
||||
|
||||
result = []
|
||||
async with self.exit_stack() as stack:
|
||||
stack.push_async_exit(cm)
|
||||
self.assertEqual(result, [])
|
||||
self.assertEqual(result, [('exit', None, None, None)])
|
||||
|
||||
@_async_test
|
||||
async def test_enter_async_context_errors(self):
|
||||
class LacksEnterAndExit:
|
||||
|
||||
Vendored
+12
@@ -186,6 +186,18 @@ class TestDefaultDict(unittest.TestCase):
|
||||
with self.assertRaises(TypeError):
|
||||
i |= None
|
||||
|
||||
# frozendict
|
||||
i_fd = i | frozendict(s)
|
||||
self.assertIs(type(i_fd), defaultdict)
|
||||
self.assertIs(i_fd.default_factory, int)
|
||||
self.assertDictEqual(i_fd, {1: "one", 2: 2, 0: "zero"})
|
||||
self.assertEqual(list(i_fd), [1, 2, 0])
|
||||
|
||||
fd_i = frozendict(s) | i
|
||||
self.assertIs(type(fd_i), frozendict)
|
||||
self.assertEqual(fd_i, {1: "one", 2: 2, 0: "zero"})
|
||||
self.assertEqual(list(fd_i), [0, 1, 2])
|
||||
|
||||
def test_factory_conflict_with_set_value(self):
|
||||
key = "conflict_test"
|
||||
count = 0
|
||||
|
||||
Vendored
+28
-4
@@ -1,5 +1,7 @@
|
||||
import difflib
|
||||
from test.support import findfile
|
||||
from test import support
|
||||
from test.support import findfile, force_colorized
|
||||
from test.support.import_helper import ensure_lazy_imports
|
||||
import unittest
|
||||
import doctest
|
||||
import sys
|
||||
@@ -265,21 +267,21 @@ class TestSFpatches(unittest.TestCase):
|
||||
html_diff = difflib.HtmlDiff()
|
||||
output = html_diff.make_file(patch914575_from1.splitlines(),
|
||||
patch914575_to1.splitlines())
|
||||
self.assertIn('content="text/html; charset=utf-8"', output)
|
||||
self.assertIn('charset="utf-8"', output)
|
||||
|
||||
def test_make_file_iso88591_charset(self):
|
||||
html_diff = difflib.HtmlDiff()
|
||||
output = html_diff.make_file(patch914575_from1.splitlines(),
|
||||
patch914575_to1.splitlines(),
|
||||
charset='iso-8859-1')
|
||||
self.assertIn('content="text/html; charset=iso-8859-1"', output)
|
||||
self.assertIn('charset="iso-8859-1"', output)
|
||||
|
||||
def test_make_file_usascii_charset_with_nonascii_input(self):
|
||||
html_diff = difflib.HtmlDiff()
|
||||
output = html_diff.make_file(patch914575_nonascii_from1.splitlines(),
|
||||
patch914575_nonascii_to1.splitlines(),
|
||||
charset='us-ascii')
|
||||
self.assertIn('content="text/html; charset=us-ascii"', output)
|
||||
self.assertIn('charset="us-ascii"', output)
|
||||
self.assertIn('ımplıcıt', output)
|
||||
|
||||
def test_strip_trailing_newlines_before_diff(self):
|
||||
@@ -397,6 +399,22 @@ class TestOutputFormat(unittest.TestCase):
|
||||
self.assertEqual(fmt(3,6), '4,6')
|
||||
self.assertEqual(fmt(0,0), '0')
|
||||
|
||||
@force_colorized
|
||||
def test_unified_diff_colored_output(self):
|
||||
args = [['one', 'three'], ['two', 'three'], 'Original', 'Current',
|
||||
'2005-01-26 23:30:50', '2010-04-02 10:20:52']
|
||||
actual = list(difflib.unified_diff(*args, lineterm='', color=True))
|
||||
|
||||
expect = [
|
||||
"\033[1m--- Original\t2005-01-26 23:30:50\033[0m",
|
||||
"\033[1m+++ Current\t2010-04-02 10:20:52\033[0m",
|
||||
"\033[36m@@ -1,2 +1,2 @@\033[0m",
|
||||
"\033[31m-one\033[0m",
|
||||
"\033[32m+two\033[0m",
|
||||
"\033[0m three\033[0m",
|
||||
]
|
||||
self.assertEqual(expect, actual)
|
||||
|
||||
|
||||
class TestBytes(unittest.TestCase):
|
||||
# don't really care about the content of the output, just the fact
|
||||
@@ -651,6 +669,12 @@ def setUpModule():
|
||||
difflib.HtmlDiff._default_prefix = 0
|
||||
|
||||
|
||||
class LazyImportTest(unittest.TestCase):
|
||||
@support.cpython_only
|
||||
def test_lazy_import(self):
|
||||
ensure_lazy_imports("difflib", {"_colorize"})
|
||||
|
||||
|
||||
def load_tests(loader, tests, pattern):
|
||||
tests.addTest(doctest.DocTestSuite(difflib))
|
||||
return tests
|
||||
|
||||
Vendored
+177
-155
@@ -1,22 +1,42 @@
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
<html>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type"
|
||||
content="text/html; charset=utf-8" />
|
||||
<title></title>
|
||||
<style type="text/css">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Diff comparison</title>
|
||||
<style>
|
||||
:root {color-scheme: light dark}
|
||||
table.diff {font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; border:medium}
|
||||
.diff_header {background-color:#e0e0e0}
|
||||
td.diff_header {text-align:right}
|
||||
.diff_next {background-color:#c0c0c0}
|
||||
table.diff {
|
||||
font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace;
|
||||
border: medium;
|
||||
}
|
||||
.diff_header {
|
||||
background-color: #e0e0e0;
|
||||
font-weight: bold;
|
||||
}
|
||||
td.diff_header {
|
||||
text-align: right;
|
||||
padding: 0 8px;
|
||||
}
|
||||
.diff_next {
|
||||
background-color: #c0c0c0;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.diff_add {background-color:palegreen}
|
||||
.diff_chg {background-color:#ffff77}
|
||||
.diff_sub {background-color:#ffaaaa}
|
||||
table.diff[summary="Legends"] {
|
||||
margin-top: 20px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
table.diff[summary="Legends"] th {
|
||||
background-color: #e0e0e0;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
table.diff[summary="Legends"] td {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.diff_header {background-color:#666}
|
||||
@@ -24,6 +44,8 @@
|
||||
.diff_add {background-color:darkgreen}
|
||||
.diff_chg {background-color:#847415}
|
||||
.diff_sub {background-color:darkred}
|
||||
table.diff[summary="Legends"] {border-color:#555}
|
||||
table.diff[summary="Legends"] th{background-color:#666}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -37,11 +59,11 @@
|
||||
<thead><tr><th class="diff_next"><br /></th><th colspan="2" class="diff_header">from</th><th class="diff_next"><br /></th><th colspan="2" class="diff_header">to</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to0__0"><a href="#difflib_chg_to0__0">f</a></td><td class="diff_header" id="from0_1">1</td><td nowrap="nowrap"></td><td class="diff_next"><a href="#difflib_chg_to0__0">f</a></td><td class="diff_header" id="to0_1">1</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to0__1">n</a></td><td class="diff_header" id="from0_2">2</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to0__1">n</a></td><td class="diff_header" id="to0_2">2</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_3">3</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_4">4</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to0_3">3</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_5">5</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to0_4">4</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to0_5">5</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to0__1">n</a></td><td class="diff_header" id="from0_2">2</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to0__1">n</a></td><td class="diff_header" id="to0_2">2</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_3">3</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_4">4</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to0_3">3</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_5">5</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to0_4">4</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to0_5">5</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_6">6</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_6">6</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_7">7</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_7">7</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_8">8</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_8">8</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -53,11 +75,11 @@
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_14">14</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_14">14</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_15">15</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_15">15</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_16">16</td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to0_16">16</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to0__2">n</a></td><td class="diff_header" id="from0_17">17</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to0__2">n</a></td><td class="diff_header" id="to0_17">17</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_18">18</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_19">19</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to0_18">18</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_20">20</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to0_19">19</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to0_20">20</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to0__2">n</a></td><td class="diff_header" id="from0_17">17</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to0__2">n</a></td><td class="diff_header" id="to0_17">17</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_18">18</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_19">19</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to0_18">18</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_20">20</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to0_19">19</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to0_20">20</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_21">21</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_21">21</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_22">22</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_22">22</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_23">23</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_23">23</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -69,11 +91,11 @@
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_29">29</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_29">29</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_30">30</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_30">30</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_31">31</td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to0_31">31</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to0__top">t</a></td><td class="diff_header" id="from0_32">32</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to0__top">t</a></td><td class="diff_header" id="to0_32">32</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_33">33</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_34">34</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to0_33">33</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_35">35</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to0_34">34</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to0_35">35</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to0__top">t</a></td><td class="diff_header" id="from0_32">32</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to0__top">t</a></td><td class="diff_header" id="to0_32">32</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_33">33</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_34">34</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to0_33">33</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_35">35</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to0_34">34</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to0_35">35</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_36">36</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_36">36</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_37">37</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_37">37</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from0_38">38</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to0_38">38</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -111,11 +133,11 @@
|
||||
<thead><tr><th class="diff_next"><br /></th><th colspan="2" class="diff_header">from</th><th class="diff_next"><br /></th><th colspan="2" class="diff_header">to</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to1__0"><a href="#difflib_chg_to1__0">f</a></td><td class="diff_header" id="from1_1">1</td><td nowrap="nowrap"></td><td class="diff_next"><a href="#difflib_chg_to1__0">f</a></td><td class="diff_header" id="to1_1">1</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to1__1">n</a></td><td class="diff_header" id="from1_2">2</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to1__1">n</a></td><td class="diff_header" id="to1_2">2</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_3">3</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_4">4</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to1_3">3</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_5">5</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to1_4">4</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to1_5">5</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to1__1">n</a></td><td class="diff_header" id="from1_2">2</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to1__1">n</a></td><td class="diff_header" id="to1_2">2</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_3">3</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_4">4</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to1_3">3</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_5">5</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to1_4">4</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to1_5">5</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_6">6</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_6">6</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_7">7</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_7">7</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_8">8</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_8">8</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -128,11 +150,11 @@
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_14">14</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_14">14</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_15">15</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_15">15</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_16">16</td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to1_16">16</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to1__2">n</a></td><td class="diff_header" id="from1_17">17</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to1__2">n</a></td><td class="diff_header" id="to1_17">17</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_18">18</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_19">19</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to1_18">18</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_20">20</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to1_19">19</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to1_20">20</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to1__2">n</a></td><td class="diff_header" id="from1_17">17</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to1__2">n</a></td><td class="diff_header" id="to1_17">17</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_18">18</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_19">19</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to1_18">18</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_20">20</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to1_19">19</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to1_20">20</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_21">21</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_21">21</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_22">22</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_22">22</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_23">23</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_23">23</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -145,11 +167,11 @@
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_29">29</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_29">29</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_30">30</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_30">30</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_31">31</td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to1_31">31</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to1__top">t</a></td><td class="diff_header" id="from1_32">32</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to1__top">t</a></td><td class="diff_header" id="to1_32">32</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_33">33</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_34">34</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to1_33">33</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_35">35</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to1_34">34</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to1_35">35</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to1__top">t</a></td><td class="diff_header" id="from1_32">32</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to1__top">t</a></td><td class="diff_header" id="to1_32">32</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_33">33</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_34">34</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to1_33">33</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_35">35</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to1_34">34</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to1_35">35</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_36">36</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_36">36</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_37">37</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_37">37</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from1_38">38</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to1_38">38</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -170,11 +192,11 @@
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_9">9</td><td nowrap="nowrap">456</td><td class="diff_next"></td><td class="diff_header" id="to2_9">9</td><td nowrap="nowrap">456</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_10">10</td><td nowrap="nowrap">456</td><td class="diff_next"></td><td class="diff_header" id="to2_10">10</td><td nowrap="nowrap">456</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_11">11</td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to2_11">11</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to2__1">n</a></td><td class="diff_header" id="from2_12">12</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to2__1">n</a></td><td class="diff_header" id="to2_12">12</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_13">13</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_14">14</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to2_13">13</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_15">15</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to2_14">14</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to2_15">15</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to2__1">n</a></td><td class="diff_header" id="from2_12">12</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to2__1">n</a></td><td class="diff_header" id="to2_12">12</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_13">13</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_14">14</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to2_13">13</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_15">15</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to2_14">14</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to2_15">15</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_16">16</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_16">16</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_17">17</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_17">17</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_18">18</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_18">18</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -187,11 +209,11 @@
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_24">24</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_24">24</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_25">25</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_25">25</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_26">26</td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to2_26">26</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to2__2">n</a></td><td class="diff_header" id="from2_27">27</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to2__2">n</a></td><td class="diff_header" id="to2_27">27</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_28">28</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_29">29</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to2_28">28</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_30">30</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to2_29">29</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to2_30">30</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to2__2">n</a></td><td class="diff_header" id="from2_27">27</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to2__2">n</a></td><td class="diff_header" id="to2_27">27</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_28">28</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_29">29</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to2_28">28</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_30">30</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to2_29">29</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to2_30">30</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_31">31</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_31">31</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_32">32</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_32">32</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_33">33</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_33">33</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -204,11 +226,11 @@
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_39">39</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_39">39</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_40">40</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_40">40</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_41">41</td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to2_41">41</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to2__top">t</a></td><td class="diff_header" id="from2_42">42</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to2__top">t</a></td><td class="diff_header" id="to2_42">42</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_43">43</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_44">44</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to2_43">43</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_45">45</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to2_44">44</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to2_45">45</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to2__top">t</a></td><td class="diff_header" id="from2_42">42</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to2__top">t</a></td><td class="diff_header" id="to2_42">42</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_43">43</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_44">44</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to2_43">43</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_45">45</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to2_44">44</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to2_45">45</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_46">46</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_46">46</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_47">47</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_47">47</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from2_48">48</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to2_48">48</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -225,11 +247,11 @@
|
||||
<thead><tr><th class="diff_next"><br /></th><th colspan="2" class="diff_header">from</th><th class="diff_next"><br /></th><th colspan="2" class="diff_header">to</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to3__0"><a href="#difflib_chg_to3__0">f</a></td><td class="diff_header" id="from3_1">1</td><td nowrap="nowrap"></td><td class="diff_next"><a href="#difflib_chg_to3__0">f</a></td><td class="diff_header" id="to3_1">1</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to3__1">n</a></td><td class="diff_header" id="from3_2">2</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to3__1">n</a></td><td class="diff_header" id="to3_2">2</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_3">3</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_4">4</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to3_3">3</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_5">5</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to3_4">4</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to3_5">5</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to3__1">n</a></td><td class="diff_header" id="from3_2">2</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to3__1">n</a></td><td class="diff_header" id="to3_2">2</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_3">3</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_4">4</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to3_3">3</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_5">5</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to3_4">4</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to3_5">5</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_6">6</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_6">6</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_7">7</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_7">7</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_8">8</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_8">8</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -241,11 +263,11 @@
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_14">14</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_14">14</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_15">15</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_15">15</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_16">16</td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to3_16">16</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to3__2">n</a></td><td class="diff_header" id="from3_17">17</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to3__2">n</a></td><td class="diff_header" id="to3_17">17</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_18">18</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_19">19</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to3_18">18</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_20">20</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to3_19">19</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to3_20">20</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to3__2">n</a></td><td class="diff_header" id="from3_17">17</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to3__2">n</a></td><td class="diff_header" id="to3_17">17</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_18">18</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_19">19</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to3_18">18</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_20">20</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to3_19">19</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to3_20">20</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_21">21</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_21">21</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_22">22</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_22">22</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_23">23</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_23">23</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -257,11 +279,11 @@
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_29">29</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_29">29</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_30">30</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_30">30</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_31">31</td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to3_31">31</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to3__top">t</a></td><td class="diff_header" id="from3_32">32</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to3__top">t</a></td><td class="diff_header" id="to3_32">32</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_33">33</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_34">34</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to3_33">33</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_35">35</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to3_34">34</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to3_35">35</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to3__top">t</a></td><td class="diff_header" id="from3_32">32</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to3__top">t</a></td><td class="diff_header" id="to3_32">32</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_33">33</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_34">34</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to3_33">33</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_35">35</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to3_34">34</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to3_35">35</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_36">36</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_36">36</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_37">37</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_37">37</td><td nowrap="nowrap">123</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from3_38">38</td><td nowrap="nowrap">123</td><td class="diff_next"></td><td class="diff_header" id="to3_38">38</td><td nowrap="nowrap">123</td></tr>
|
||||
@@ -278,25 +300,25 @@
|
||||
<colgroup></colgroup> <colgroup></colgroup> <colgroup></colgroup>
|
||||
<thead><tr><th class="diff_next"><br /></th><th colspan="2" class="diff_header">from</th><th class="diff_next"><br /></th><th colspan="2" class="diff_header">to</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to4__0"><a href="#difflib_chg_to4__1">n</a></td><td class="diff_header" id="from4_2">2</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to4__1">n</a></td><td class="diff_header" id="to4_2">2</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_3">3</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_4">4</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to4_3">3</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_5">5</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to4_4">4</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to4_5">5</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to4__0"><a href="#difflib_chg_to4__1">n</a></td><td class="diff_header" id="from4_2">2</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to4__1">n</a></td><td class="diff_header" id="to4_2">2</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_3">3</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_4">4</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to4_3">3</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_5">5</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to4_4">4</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to4_5">5</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to4__1"><a href="#difflib_chg_to4__2">n</a></td><td class="diff_header" id="from4_17">17</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to4__2">n</a></td><td class="diff_header" id="to4_17">17</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_18">18</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_19">19</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to4_18">18</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_20">20</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to4_19">19</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to4_20">20</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to4__1"><a href="#difflib_chg_to4__2">n</a></td><td class="diff_header" id="from4_17">17</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to4__2">n</a></td><td class="diff_header" id="to4_17">17</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_18">18</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_19">19</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to4_18">18</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_20">20</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to4_19">19</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to4_20">20</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to4__2"><a href="#difflib_chg_to4__top">t</a></td><td class="diff_header" id="from4_32">32</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to4__top">t</a></td><td class="diff_header" id="to4_32">32</td><td nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_33">33</td><td nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_34">34</td><td nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to4_33">33</td><td nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_35">35</td><td nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to4_34">34</td><td nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to4_35">35</td><td nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to4__2"><a href="#difflib_chg_to4__top">t</a></td><td class="diff_header" id="from4_32">32</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">TT</span>er than ugly.</td><td class="diff_next"><a href="#difflib_chg_to4__top">t</a></td><td class="diff_header" id="to4_32">32</td><td class="diff_changed" nowrap="nowrap"> 1. Beautiful is be<span class="diff_chg">tt</span>er than ugly.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_33">33</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 2. Explicit is better than implicit.</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_34">34</td><td class="diff_changed" nowrap="nowrap"> 3. Simple is better than complex.</td><td class="diff_next"></td><td class="diff_header" id="to4_33">33</td><td class="diff_changed" nowrap="nowrap"> 3.<span class="diff_add"> </span> Simple is better than complex.</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from4_35">35</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub"> 4. Complex is better than complicated.</span></td><td class="diff_next"></td><td class="diff_header" id="to4_34">34</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 4. Complicated is better than complex.</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"></td><td class="diff_next"></td><td class="diff_header" id="to4_35">35</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> 5. Flat is better than nested.</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>Same Context</h2>
|
||||
@@ -396,11 +418,11 @@
|
||||
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to9__0"><a href="#difflib_chg_to9__0">f</a></td><td class="diff_header" id="from9_1">1</td><td nowrap="nowrap"></td><td class="diff_next"><a href="#difflib_chg_to9__0">f</a></td><td class="diff_header" id="to9_1">1</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to9__top">t</a></td><td class="diff_header" id="from9_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td><td class="diff_next"><a href="#difflib_chg_to9__top">t</a></td><td class="diff_header" id="to9_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from9_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td><td class="diff_next"></td><td class="diff_header" id="to9_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from9_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td><td class="diff_next"></td><td class="diff_header" id="to9_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from9_5">5</td><td nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td><td class="diff_next"></td><td class="diff_header" id="to9_5">5</td><td nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from9_6">6</td><td nowrap="nowrap">Line 5: has from:[t] to:[ss] at end<span class="diff_sub"> </span></td><td class="diff_next"></td><td class="diff_header" id="to9_6">6</td><td nowrap="nowrap">Line 5: has from:[t] to:[ss] at end</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to9__top">t</a></td><td class="diff_header" id="from9_2">2</td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td><td class="diff_next"><a href="#difflib_chg_to9__top">t</a></td><td class="diff_header" id="to9_2">2</td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from9_3">3</td><td class="diff_changed" nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td><td class="diff_next"></td><td class="diff_header" id="to9_3">3</td><td class="diff_changed" nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from9_4">4</td><td class="diff_changed" nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td><td class="diff_next"></td><td class="diff_header" id="to9_4">4</td><td class="diff_changed" nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from9_5">5</td><td class="diff_changed" nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td><td class="diff_next"></td><td class="diff_header" id="to9_5">5</td><td class="diff_changed" nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from9_6">6</td><td class="diff_changed" nowrap="nowrap">Line 5: has from:[t] to:[ss] at end<span class="diff_sub"> </span></td><td class="diff_next"></td><td class="diff_header" id="to9_6">6</td><td class="diff_changed" nowrap="nowrap">Line 5: has from:[t] to:[ss] at end</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>tabsize=default</h2>
|
||||
@@ -412,11 +434,11 @@
|
||||
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to10__0"><a href="#difflib_chg_to10__0">f</a></td><td class="diff_header" id="from10_1">1</td><td nowrap="nowrap"></td><td class="diff_next"><a href="#difflib_chg_to10__0">f</a></td><td class="diff_header" id="to10_1">1</td><td nowrap="nowrap"></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to10__top">t</a></td><td class="diff_header" id="from10_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td><td class="diff_next"><a href="#difflib_chg_to10__top">t</a></td><td class="diff_header" id="to10_2">2</td><td nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from10_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td><td class="diff_next"></td><td class="diff_header" id="to10_3">3</td><td nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from10_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td><td class="diff_next"></td><td class="diff_header" id="to10_4">4</td><td nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from10_5">5</td><td nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td><td class="diff_next"></td><td class="diff_header" id="to10_5">5</td><td nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from10_6">6</td><td nowrap="nowrap">Line 5: has from:[t] to:[ss] at end<span class="diff_sub"> </span></td><td class="diff_next"></td><td class="diff_header" id="to10_6">6</td><td nowrap="nowrap">Line 5: has from:[t] to:[ss] at end</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to10__top">t</a></td><td class="diff_header" id="from10_2">2</td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td><td class="diff_next"><a href="#difflib_chg_to10__top">t</a></td><td class="diff_header" id="to10_2">2</td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg"> </span>Line 1: preceded by from:[tt] to:[ssss]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from10_3">3</td><td class="diff_changed" nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td><td class="diff_next"></td><td class="diff_header" id="to10_3">3</td><td class="diff_changed" nowrap="nowrap"> <span class="diff_chg"> </span> Line 2: preceded by from:[sstt] to:[sssst]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from10_4">4</td><td class="diff_changed" nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td><td class="diff_next"></td><td class="diff_header" id="to10_4">4</td><td class="diff_changed" nowrap="nowrap"> <span class="diff_chg"> </span>Line 3: preceded by from:[sstst] to:[ssssss]</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from10_5">5</td><td class="diff_changed" nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td><td class="diff_next"></td><td class="diff_header" id="to10_5">5</td><td class="diff_changed" nowrap="nowrap">Line 4: <span class="diff_chg"> </span>has from:[sst] to:[sss] after :</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from10_6">6</td><td class="diff_changed" nowrap="nowrap">Line 5: has from:[t] to:[ss] at end<span class="diff_sub"> </span></td><td class="diff_next"></td><td class="diff_header" id="to10_6">6</td><td class="diff_changed" nowrap="nowrap">Line 5: has from:[t] to:[ss] at end</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>Context (wrapcolumn=14,numlines=0)</h2>
|
||||
@@ -427,31 +449,31 @@
|
||||
<colgroup></colgroup> <colgroup></colgroup> <colgroup></colgroup>
|
||||
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to11__0"><a href="#difflib_chg_to11__1">n</a></td><td class="diff_header" id="from11_4">4</td><td nowrap="nowrap"><span class="diff_sub">line 2</span></td><td class="diff_next"><a href="#difflib_chg_to11__1">n</a></td><td class="diff_header" id="to11_4">4</td><td nowrap="nowrap"><span class="diff_add">line 2 adde</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_add">d</span></td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to11__0"><a href="#difflib_chg_to11__1">n</a></td><td class="diff_header" id="from11_4">4</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">line 2</span></td><td class="diff_next"><a href="#difflib_chg_to11__1">n</a></td><td class="diff_header" id="to11_4">4</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">line 2 adde</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">d</span></td></tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to11__1"><a href="#difflib_chg_to11__2">n</a></td><td class="diff_header" id="from11_6">6</td><td nowrap="nowrap">line 4 chan<span class="diff_chg">g</span></td><td class="diff_next"><a href="#difflib_chg_to11__2">n</a></td><td class="diff_header" id="to11_6">6</td><td nowrap="nowrap">line 4 chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from11_7">7</td><td nowrap="nowrap">line 5<span class="diff_chg"> </span> chan<span class="diff_chg">g</span></td><td class="diff_next"></td><td class="diff_header" id="to11_7">7</td><td nowrap="nowrap">line 5<span class="diff_chg">a</span> chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg"></span>ed</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg"></span>ed</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from11_8">8</td><td nowrap="nowrap">line 6<span class="diff_chg"> </span> chang</td><td class="diff_next"></td><td class="diff_header" id="to11_8">8</td><td nowrap="nowrap">line 6<span class="diff_chg">a</span> chang</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to11__1"><a href="#difflib_chg_to11__2">n</a></td><td class="diff_header" id="from11_6">6</td><td class="diff_changed" nowrap="nowrap">line 4 chan<span class="diff_chg">g</span></td><td class="diff_next"><a href="#difflib_chg_to11__2">n</a></td><td class="diff_header" id="to11_6">6</td><td class="diff_changed" nowrap="nowrap">line 4 chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from11_7">7</td><td class="diff_changed" nowrap="nowrap">line 5<span class="diff_chg"> </span> chan<span class="diff_chg">g</span></td><td class="diff_next"></td><td class="diff_header" id="to11_7">7</td><td class="diff_changed" nowrap="nowrap">line 5<span class="diff_chg">a</span> chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg"></span>ed</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg"></span>ed</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from11_8">8</td><td class="diff_changed" nowrap="nowrap">line 6<span class="diff_chg"> </span> chang</td><td class="diff_next"></td><td class="diff_header" id="to11_8">8</td><td class="diff_changed" nowrap="nowrap">line 6<span class="diff_chg">a</span> chang</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to11__2"><a href="#difflib_chg_to11__3">n</a></td><td class="diff_header" id="from11_10">10</td><td nowrap="nowrap"><span class="diff_sub">line 8 subtra</span></td><td class="diff_next"><a href="#difflib_chg_to11__3">n</a></td><td class="diff_header" id="to11_10">10</td><td nowrap="nowrap"><span class="diff_add">line 8</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_sub">cted</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to11__2"><a href="#difflib_chg_to11__3">n</a></td><td class="diff_header" id="from11_10">10</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">line 8 subtra</span></td><td class="diff_next"><a href="#difflib_chg_to11__3">n</a></td><td class="diff_header" id="to11_10">10</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">line 8</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">cted</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td></tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr><td class="diff_next" id="difflib_chg_to11__3"><a href="#difflib_chg_to11__top">t</a></td><td class="diff_header" id="from11_12">12</td><td nowrap="nowrap"><span class="diff_sub">12345678901234</span></td><td class="diff_next"><a href="#difflib_chg_to11__top">t</a></td><td class="diff_header" id="to11_12">12</td><td nowrap="nowrap"><span class="diff_add">1234567890</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_sub">56789012345689</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_sub">012345</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from11_13">13</td><td nowrap="nowrap"><span class="diff_sub">short line</span></td><td class="diff_next"></td><td class="diff_header" id="to11_13">13</td><td nowrap="nowrap"><span class="diff_add">another long l</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_add">ine that needs</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_add"> to be wrapped</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from11_14">14</td><td nowrap="nowrap">just fit<span class="diff_chg">s</span> in!!</td><td class="diff_next"></td><td class="diff_header" id="to11_14">14</td><td nowrap="nowrap">just fit<span class="diff_chg">S</span> in!!</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from11_15">15</td><td nowrap="nowrap">just fits in t</td><td class="diff_next"></td><td class="diff_header" id="to11_15">15</td><td nowrap="nowrap">just fits in t</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">wo line<span class="diff_chg">s</span> yup!!</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">wo line<span class="diff_chg">S</span> yup!!</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to11__3"><a href="#difflib_chg_to11__top">t</a></td><td class="diff_header" id="from11_12">12</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">12345678901234</span></td><td class="diff_next"><a href="#difflib_chg_to11__top">t</a></td><td class="diff_header" id="to11_12">12</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">1234567890</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">56789012345689</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">012345</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from11_13">13</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">short line</span></td><td class="diff_next"></td><td class="diff_header" id="to11_13">13</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">another long l</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">ine that needs</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> to be wrapped</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from11_14">14</td><td class="diff_changed" nowrap="nowrap">just fit<span class="diff_chg">s</span> in!!</td><td class="diff_next"></td><td class="diff_header" id="to11_14">14</td><td class="diff_changed" nowrap="nowrap">just fit<span class="diff_chg">S</span> in!!</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from11_15">15</td><td class="diff_changed" nowrap="nowrap">just fits in t</td><td class="diff_next"></td><td class="diff_header" id="to11_15">15</td><td class="diff_changed" nowrap="nowrap">just fits in t</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap">wo line<span class="diff_chg">s</span> yup!!</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap">wo line<span class="diff_chg">S</span> yup!!</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>wrapcolumn=14,splitlines()</h2>
|
||||
@@ -467,28 +489,28 @@
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">56789012345689</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">56789012345689</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to12__1"></td><td class="diff_header">></td><td nowrap="nowrap">012345</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">012345</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_3">3</td><td nowrap="nowrap">line 1</td><td class="diff_next"></td><td class="diff_header" id="to12_3">3</td><td nowrap="nowrap">line 1</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to12__1">n</a></td><td class="diff_header" id="from12_4">4</td><td nowrap="nowrap"><span class="diff_sub">line 2</span></td><td class="diff_next"><a href="#difflib_chg_to12__1">n</a></td><td class="diff_header" id="to12_4">4</td><td nowrap="nowrap"><span class="diff_add">line 2 adde</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_add">d</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to12__1">n</a></td><td class="diff_header" id="from12_4">4</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">line 2</span></td><td class="diff_next"><a href="#difflib_chg_to12__1">n</a></td><td class="diff_header" id="to12_4">4</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">line 2 adde</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">d</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_5">5</td><td nowrap="nowrap">line 3</td><td class="diff_next"></td><td class="diff_header" id="to12_5">5</td><td nowrap="nowrap">line 3</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to12__2">n</a></td><td class="diff_header" id="from12_6">6</td><td nowrap="nowrap">line 4 chan<span class="diff_chg">g</span></td><td class="diff_next"><a href="#difflib_chg_to12__2">n</a></td><td class="diff_header" id="to12_6">6</td><td nowrap="nowrap">line 4 chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to12__2"></td><td class="diff_header" id="from12_7">7</td><td nowrap="nowrap">line 5<span class="diff_chg"> </span> chan<span class="diff_chg">g</span></td><td class="diff_next"></td><td class="diff_header" id="to12_7">7</td><td nowrap="nowrap">line 5<span class="diff_chg">a</span> chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg"></span>ed</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg"></span>ed</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_8">8</td><td nowrap="nowrap">line 6<span class="diff_chg"> </span> chang</td><td class="diff_next"></td><td class="diff_header" id="to12_8">8</td><td nowrap="nowrap">line 6<span class="diff_chg">a</span> chang</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to12__3"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to12__2">n</a></td><td class="diff_header" id="from12_6">6</td><td class="diff_changed" nowrap="nowrap">line 4 chan<span class="diff_chg">g</span></td><td class="diff_next"><a href="#difflib_chg_to12__2">n</a></td><td class="diff_header" id="to12_6">6</td><td class="diff_changed" nowrap="nowrap">line 4 chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to12__2"></td><td class="diff_header" id="from12_7">7</td><td class="diff_changed" nowrap="nowrap">line 5<span class="diff_chg"> </span> chan<span class="diff_chg">g</span></td><td class="diff_next"></td><td class="diff_header" id="to12_7">7</td><td class="diff_changed" nowrap="nowrap">line 5<span class="diff_chg">a</span> chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg"></span>ed</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg"></span>ed</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_8">8</td><td class="diff_changed" nowrap="nowrap">line 6<span class="diff_chg"> </span> chang</td><td class="diff_next"></td><td class="diff_header" id="to12_8">8</td><td class="diff_changed" nowrap="nowrap">line 6<span class="diff_chg">a</span> chang</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to12__3"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_9">9</td><td nowrap="nowrap">line 7</td><td class="diff_next"></td><td class="diff_header" id="to12_9">9</td><td nowrap="nowrap">line 7</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to12__3">n</a></td><td class="diff_header" id="from12_10">10</td><td nowrap="nowrap"><span class="diff_sub">line 8 subtra</span></td><td class="diff_next"><a href="#difflib_chg_to12__3">n</a></td><td class="diff_header" id="to12_10">10</td><td nowrap="nowrap"><span class="diff_add">line 8</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_sub">cted</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to12__3">n</a></td><td class="diff_header" id="from12_10">10</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">line 8 subtra</span></td><td class="diff_next"><a href="#difflib_chg_to12__3">n</a></td><td class="diff_header" id="to12_10">10</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">line 8</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">cted</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_11">11</td><td nowrap="nowrap">line 9</td><td class="diff_next"></td><td class="diff_header" id="to12_11">11</td><td nowrap="nowrap">line 9</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to12__top">t</a></td><td class="diff_header" id="from12_12">12</td><td nowrap="nowrap"><span class="diff_sub">12345678901234</span></td><td class="diff_next"><a href="#difflib_chg_to12__top">t</a></td><td class="diff_header" id="to12_12">12</td><td nowrap="nowrap"><span class="diff_add">1234567890</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_sub">56789012345689</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_sub">012345</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_13">13</td><td nowrap="nowrap"><span class="diff_sub">short line</span></td><td class="diff_next"></td><td class="diff_header" id="to12_13">13</td><td nowrap="nowrap"><span class="diff_add">another long l</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_add">ine that needs</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_add"> to be wrapped</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_14">14</td><td nowrap="nowrap">just fit<span class="diff_chg">s</span> in!!</td><td class="diff_next"></td><td class="diff_header" id="to12_14">14</td><td nowrap="nowrap">just fit<span class="diff_chg">S</span> in!!</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_15">15</td><td nowrap="nowrap">just fits in t</td><td class="diff_next"></td><td class="diff_header" id="to12_15">15</td><td nowrap="nowrap">just fits in t</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">wo line<span class="diff_chg">s</span> yup!!</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">wo line<span class="diff_chg">S</span> yup!!</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to12__top">t</a></td><td class="diff_header" id="from12_12">12</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">12345678901234</span></td><td class="diff_next"><a href="#difflib_chg_to12__top">t</a></td><td class="diff_header" id="to12_12">12</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">1234567890</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">56789012345689</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">012345</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_13">13</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">short line</span></td><td class="diff_next"></td><td class="diff_header" id="to12_13">13</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">another long l</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">ine that needs</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> to be wrapped</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_14">14</td><td class="diff_changed" nowrap="nowrap">just fit<span class="diff_chg">s</span> in!!</td><td class="diff_next"></td><td class="diff_header" id="to12_14">14</td><td class="diff_changed" nowrap="nowrap">just fit<span class="diff_chg">S</span> in!!</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_15">15</td><td class="diff_changed" nowrap="nowrap">just fits in t</td><td class="diff_next"></td><td class="diff_header" id="to12_15">15</td><td class="diff_changed" nowrap="nowrap">just fits in t</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap">wo line<span class="diff_chg">s</span> yup!!</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap">wo line<span class="diff_chg">S</span> yup!!</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from12_16">16</td><td nowrap="nowrap">the end</td><td class="diff_next"></td><td class="diff_header" id="to12_16">16</td><td nowrap="nowrap">the end</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -505,28 +527,28 @@
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">56789012345689</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">56789012345689</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to13__1"></td><td class="diff_header">></td><td nowrap="nowrap">012345</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">012345</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_3">3</td><td nowrap="nowrap">line 1</td><td class="diff_next"></td><td class="diff_header" id="to13_3">3</td><td nowrap="nowrap">line 1</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to13__1">n</a></td><td class="diff_header" id="from13_4">4</td><td nowrap="nowrap"><span class="diff_sub">line 2</span></td><td class="diff_next"><a href="#difflib_chg_to13__1">n</a></td><td class="diff_header" id="to13_4">4</td><td nowrap="nowrap"><span class="diff_add">line 2 adde</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_add">d</span></td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to13__1">n</a></td><td class="diff_header" id="from13_4">4</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">line 2</span></td><td class="diff_next"><a href="#difflib_chg_to13__1">n</a></td><td class="diff_header" id="to13_4">4</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">line 2 adde</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">d</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_5">5</td><td nowrap="nowrap">line 3</td><td class="diff_next"></td><td class="diff_header" id="to13_5">5</td><td nowrap="nowrap">line 3</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to13__2">n</a></td><td class="diff_header" id="from13_6">6</td><td nowrap="nowrap">line 4 chan<span class="diff_chg">g</span></td><td class="diff_next"><a href="#difflib_chg_to13__2">n</a></td><td class="diff_header" id="to13_6">6</td><td nowrap="nowrap">line 4 chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to13__2"></td><td class="diff_header" id="from13_7">7</td><td nowrap="nowrap">line 5<span class="diff_chg"> </span> chan<span class="diff_chg">g</span></td><td class="diff_next"></td><td class="diff_header" id="to13_7">7</td><td nowrap="nowrap">line 5<span class="diff_chg">a</span> chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg"></span>ed</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg"></span>ed</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_8">8</td><td nowrap="nowrap">line 6<span class="diff_chg"> </span> chang</td><td class="diff_next"></td><td class="diff_header" id="to13_8">8</td><td nowrap="nowrap">line 6<span class="diff_chg">a</span> chang</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to13__3"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to13__2">n</a></td><td class="diff_header" id="from13_6">6</td><td class="diff_changed" nowrap="nowrap">line 4 chan<span class="diff_chg">g</span></td><td class="diff_next"><a href="#difflib_chg_to13__2">n</a></td><td class="diff_header" id="to13_6">6</td><td class="diff_changed" nowrap="nowrap">line 4 chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to13__2"></td><td class="diff_header" id="from13_7">7</td><td class="diff_changed" nowrap="nowrap">line 5<span class="diff_chg"> </span> chan<span class="diff_chg">g</span></td><td class="diff_next"></td><td class="diff_header" id="to13_7">7</td><td class="diff_changed" nowrap="nowrap">line 5<span class="diff_chg">a</span> chan<span class="diff_chg">G</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg"></span>ed</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg"></span>ed</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_8">8</td><td class="diff_changed" nowrap="nowrap">line 6<span class="diff_chg"> </span> chang</td><td class="diff_next"></td><td class="diff_header" id="to13_8">8</td><td class="diff_changed" nowrap="nowrap">line 6<span class="diff_chg">a</span> chang</td></tr>
|
||||
<tr><td class="diff_next" id="difflib_chg_to13__3"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">e</span>d</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_chg">E</span>d</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_9">9</td><td nowrap="nowrap">line 7</td><td class="diff_next"></td><td class="diff_header" id="to13_9">9</td><td nowrap="nowrap">line 7</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to13__3">n</a></td><td class="diff_header" id="from13_10">10</td><td nowrap="nowrap"><span class="diff_sub">line 8 subtra</span></td><td class="diff_next"><a href="#difflib_chg_to13__3">n</a></td><td class="diff_header" id="to13_10">10</td><td nowrap="nowrap"><span class="diff_add">line 8</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_sub">cted</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to13__3">n</a></td><td class="diff_header" id="from13_10">10</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">line 8 subtra</span></td><td class="diff_next"><a href="#difflib_chg_to13__3">n</a></td><td class="diff_header" id="to13_10">10</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">line 8</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">cted</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_11">11</td><td nowrap="nowrap">line 9</td><td class="diff_next"></td><td class="diff_header" id="to13_11">11</td><td nowrap="nowrap">line 9</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to13__top">t</a></td><td class="diff_header" id="from13_12">12</td><td nowrap="nowrap"><span class="diff_sub">12345678901234</span></td><td class="diff_next"><a href="#difflib_chg_to13__top">t</a></td><td class="diff_header" id="to13_12">12</td><td nowrap="nowrap"><span class="diff_add">1234567890</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_sub">56789012345689</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_sub">012345</span></td><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_13">13</td><td nowrap="nowrap"><span class="diff_sub">short line</span></td><td class="diff_next"></td><td class="diff_header" id="to13_13">13</td><td nowrap="nowrap"><span class="diff_add">another long l</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_add">ine that needs</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap"><span class="diff_add"> to be wrapped</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_14">14</td><td nowrap="nowrap">just fit<span class="diff_chg">s</span> in!!</td><td class="diff_next"></td><td class="diff_header" id="to13_14">14</td><td nowrap="nowrap">just fit<span class="diff_chg">S</span> in!!</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_15">15</td><td nowrap="nowrap">just fits in t</td><td class="diff_next"></td><td class="diff_header" id="to13_15">15</td><td nowrap="nowrap">just fits in t</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">wo line<span class="diff_chg">s</span> yup!!</td><td class="diff_next"></td><td class="diff_header">></td><td nowrap="nowrap">wo line<span class="diff_chg">S</span> yup!!</td></tr>
|
||||
<tr><td class="diff_next"><a href="#difflib_chg_to13__top">t</a></td><td class="diff_header" id="from13_12">12</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">12345678901234</span></td><td class="diff_next"><a href="#difflib_chg_to13__top">t</a></td><td class="diff_header" id="to13_12">12</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">1234567890</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">56789012345689</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">012345</span></td><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_13">13</td><td class="diff_changed" nowrap="nowrap"><span class="diff_sub">short line</span></td><td class="diff_next"></td><td class="diff_header" id="to13_13">13</td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">another long l</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_add">ine that needs</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header"></td><td class="diff_changed" nowrap="nowrap"> </td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap"><span class="diff_add"> to be wrapped</span></td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_14">14</td><td class="diff_changed" nowrap="nowrap">just fit<span class="diff_chg">s</span> in!!</td><td class="diff_next"></td><td class="diff_header" id="to13_14">14</td><td class="diff_changed" nowrap="nowrap">just fit<span class="diff_chg">S</span> in!!</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_15">15</td><td class="diff_changed" nowrap="nowrap">just fits in t</td><td class="diff_next"></td><td class="diff_header" id="to13_15">15</td><td class="diff_changed" nowrap="nowrap">just fits in t</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap">wo line<span class="diff_chg">s</span> yup!!</td><td class="diff_next"></td><td class="diff_header">></td><td class="diff_changed" nowrap="nowrap">wo line<span class="diff_chg">S</span> yup!!</td></tr>
|
||||
<tr><td class="diff_next"></td><td class="diff_header" id="from13_16">16</td><td nowrap="nowrap">the end</td><td class="diff_next"></td><td class="diff_header" id="to13_16">16</td><td nowrap="nowrap">the end</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
+282
-183
@@ -833,6 +833,118 @@ class TestDocTestFinder(unittest.TestCase):
|
||||
self.assertEqual(len(include_empty_finder.find(mod)), 1)
|
||||
self.assertEqual(len(exclude_empty_finder.find(mod)), 0)
|
||||
|
||||
def test_lineno_of_test_dict_strings(self):
|
||||
"""Test line numbers are found for __test__ dict strings."""
|
||||
module_content = '''\
|
||||
"""Module docstring."""
|
||||
|
||||
def dummy_function():
|
||||
"""Dummy function docstring."""
|
||||
pass
|
||||
|
||||
__test__ = {
|
||||
'test_string': """
|
||||
This is a test string.
|
||||
>>> 1 + 1
|
||||
2
|
||||
""",
|
||||
}
|
||||
'''
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
module_path = os.path.join(tmpdir, 'test_module_lineno.py')
|
||||
with open(module_path, 'w') as f:
|
||||
f.write(module_content)
|
||||
|
||||
sys.path.insert(0, tmpdir)
|
||||
try:
|
||||
import test_module_lineno
|
||||
finder = doctest.DocTestFinder()
|
||||
tests = finder.find(test_module_lineno)
|
||||
|
||||
test_dict_test = None
|
||||
for test in tests:
|
||||
if '__test__' in test.name:
|
||||
test_dict_test = test
|
||||
break
|
||||
|
||||
self.assertIsNotNone(
|
||||
test_dict_test,
|
||||
"__test__ dict test not found"
|
||||
)
|
||||
# gh-69113: line number should not be None for __test__ strings
|
||||
self.assertIsNotNone(
|
||||
test_dict_test.lineno,
|
||||
"Line number should not be None for __test__ dict strings"
|
||||
)
|
||||
self.assertGreater(
|
||||
test_dict_test.lineno,
|
||||
0,
|
||||
"Line number should be positive"
|
||||
)
|
||||
finally:
|
||||
if 'test_module_lineno' in sys.modules:
|
||||
del sys.modules['test_module_lineno']
|
||||
sys.path.pop(0)
|
||||
|
||||
def test_lineno_multiline_matching(self):
|
||||
"""Test multi-line matching when no unique line exists."""
|
||||
# gh-69113: test that line numbers are found even when lines
|
||||
# appear multiple times (e.g., ">>> x = 1" in both test entries)
|
||||
module_content = '''\
|
||||
"""Module docstring."""
|
||||
|
||||
__test__ = {
|
||||
'test_one': """
|
||||
>>> x = 1
|
||||
>>> x
|
||||
1
|
||||
""",
|
||||
'test_two': """
|
||||
>>> x = 1
|
||||
>>> x
|
||||
2
|
||||
""",
|
||||
}
|
||||
'''
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
module_path = os.path.join(tmpdir, 'test_module_multiline.py')
|
||||
with open(module_path, 'w') as f:
|
||||
f.write(module_content)
|
||||
|
||||
sys.path.insert(0, tmpdir)
|
||||
try:
|
||||
import test_module_multiline
|
||||
finder = doctest.DocTestFinder()
|
||||
tests = finder.find(test_module_multiline)
|
||||
|
||||
test_one = None
|
||||
test_two = None
|
||||
for test in tests:
|
||||
if 'test_one' in test.name:
|
||||
test_one = test
|
||||
elif 'test_two' in test.name:
|
||||
test_two = test
|
||||
|
||||
self.assertIsNotNone(test_one, "test_one not found")
|
||||
self.assertIsNotNone(test_two, "test_two not found")
|
||||
self.assertIsNotNone(
|
||||
test_one.lineno,
|
||||
"Line number should not be None for test_one"
|
||||
)
|
||||
self.assertIsNotNone(
|
||||
test_two.lineno,
|
||||
"Line number should not be None for test_two"
|
||||
)
|
||||
self.assertNotEqual(
|
||||
test_one.lineno,
|
||||
test_two.lineno,
|
||||
"test_one and test_two should have different line numbers"
|
||||
)
|
||||
finally:
|
||||
if 'test_module_multiline' in sys.modules:
|
||||
del sys.modules['test_module_multiline']
|
||||
sys.path.pop(0)
|
||||
|
||||
def test_DocTestParser(): r"""
|
||||
Unit tests for the `DocTestParser` class.
|
||||
|
||||
@@ -2273,20 +2385,22 @@ def test_DocTestSuite():
|
||||
>>> suite = doctest.DocTestSuite(test.test_doctest.sample_doctest)
|
||||
>>> result = suite.run(unittest.TestResult())
|
||||
>>> result
|
||||
<unittest.result.TestResult run=9 errors=0 failures=4>
|
||||
<unittest.result.TestResult run=9 errors=2 failures=2>
|
||||
>>> for tst, _ in result.failures:
|
||||
... print(tst)
|
||||
bad (test.test_doctest.sample_doctest.__test__)
|
||||
foo (test.test_doctest.sample_doctest)
|
||||
test_silly_setup (test.test_doctest.sample_doctest)
|
||||
y_is_one (test.test_doctest.sample_doctest)
|
||||
bad (test.test_doctest.sample_doctest.__test__) [0]
|
||||
foo (test.test_doctest.sample_doctest) [0]
|
||||
>>> for tst, _ in result.errors:
|
||||
... print(tst)
|
||||
test_silly_setup (test.test_doctest.sample_doctest) [1]
|
||||
y_is_one (test.test_doctest.sample_doctest) [0]
|
||||
|
||||
We can also supply the module by name:
|
||||
|
||||
>>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest')
|
||||
>>> result = suite.run(unittest.TestResult())
|
||||
>>> result
|
||||
<unittest.result.TestResult run=9 errors=0 failures=4>
|
||||
<unittest.result.TestResult run=9 errors=2 failures=2>
|
||||
|
||||
The module need not contain any doctest examples:
|
||||
|
||||
@@ -2308,21 +2422,26 @@ def test_DocTestSuite():
|
||||
>>> result
|
||||
<unittest.result.TestResult run=6 errors=0 failures=2>
|
||||
>>> len(result.skipped)
|
||||
2
|
||||
7
|
||||
>>> for tst, _ in result.skipped:
|
||||
... print(tst)
|
||||
double_skip (test.test_doctest.sample_doctest_skip) [0]
|
||||
double_skip (test.test_doctest.sample_doctest_skip) [1]
|
||||
double_skip (test.test_doctest.sample_doctest_skip)
|
||||
partial_skip_fail (test.test_doctest.sample_doctest_skip) [0]
|
||||
partial_skip_pass (test.test_doctest.sample_doctest_skip) [0]
|
||||
single_skip (test.test_doctest.sample_doctest_skip) [0]
|
||||
single_skip (test.test_doctest.sample_doctest_skip)
|
||||
>>> for tst, _ in result.failures:
|
||||
... print(tst)
|
||||
no_skip_fail (test.test_doctest.sample_doctest_skip)
|
||||
partial_skip_fail (test.test_doctest.sample_doctest_skip)
|
||||
no_skip_fail (test.test_doctest.sample_doctest_skip) [0]
|
||||
partial_skip_fail (test.test_doctest.sample_doctest_skip) [1]
|
||||
|
||||
We can use the current module:
|
||||
|
||||
>>> suite = test.test_doctest.sample_doctest.test_suite()
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=9 errors=0 failures=4>
|
||||
<unittest.result.TestResult run=9 errors=2 failures=2>
|
||||
|
||||
We can also provide a DocTestFinder:
|
||||
|
||||
@@ -2330,7 +2449,7 @@ def test_DocTestSuite():
|
||||
>>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest',
|
||||
... test_finder=finder)
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=9 errors=0 failures=4>
|
||||
<unittest.result.TestResult run=9 errors=2 failures=2>
|
||||
|
||||
The DocTestFinder need not return any tests:
|
||||
|
||||
@@ -2346,7 +2465,7 @@ def test_DocTestSuite():
|
||||
|
||||
>>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest', globs={})
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=9 errors=0 failures=5>
|
||||
<unittest.result.TestResult run=9 errors=3 failures=2>
|
||||
|
||||
Alternatively, we can provide extra globals. Here we'll make an
|
||||
error go away by providing an extra global variable:
|
||||
@@ -2354,7 +2473,7 @@ def test_DocTestSuite():
|
||||
>>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest',
|
||||
... extraglobs={'y': 1})
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=9 errors=0 failures=3>
|
||||
<unittest.result.TestResult run=9 errors=1 failures=2>
|
||||
|
||||
You can pass option flags. Here we'll cause an extra error
|
||||
by disabling the blank-line feature:
|
||||
@@ -2362,7 +2481,7 @@ def test_DocTestSuite():
|
||||
>>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest',
|
||||
... optionflags=doctest.DONT_ACCEPT_BLANKLINE)
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=9 errors=0 failures=5>
|
||||
<unittest.result.TestResult run=9 errors=2 failures=3>
|
||||
|
||||
You can supply setUp and tearDown functions:
|
||||
|
||||
@@ -2379,7 +2498,7 @@ def test_DocTestSuite():
|
||||
>>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest',
|
||||
... setUp=setUp, tearDown=tearDown)
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=9 errors=0 failures=3>
|
||||
<unittest.result.TestResult run=9 errors=1 failures=2>
|
||||
|
||||
But the tearDown restores sanity:
|
||||
|
||||
@@ -2397,7 +2516,7 @@ def test_DocTestSuite():
|
||||
|
||||
>>> suite = doctest.DocTestSuite('test.test_doctest.sample_doctest', setUp=setUp)
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=9 errors=0 failures=3>
|
||||
<unittest.result.TestResult run=9 errors=1 failures=2>
|
||||
|
||||
Here, we didn't need to use a tearDown function because we
|
||||
modified the test globals, which are a copy of the
|
||||
@@ -2413,115 +2532,99 @@ def test_DocTestSuite_errors():
|
||||
>>> suite = doctest.DocTestSuite(mod)
|
||||
>>> result = suite.run(unittest.TestResult())
|
||||
>>> result
|
||||
<unittest.result.TestResult run=4 errors=0 failures=4>
|
||||
<unittest.result.TestResult run=4 errors=6 failures=3>
|
||||
>>> print(result.failures[0][1]) # doctest: +ELLIPSIS
|
||||
AssertionError: Failed doctest test for test.test_doctest.sample_doctest_errors
|
||||
File "...sample_doctest_errors.py", line 0, in sample_doctest_errors
|
||||
<BLANKLINE>
|
||||
----------------------------------------------------------------------
|
||||
File "...sample_doctest_errors.py", line 5, in test.test_doctest.sample_doctest_errors
|
||||
Failed example:
|
||||
Traceback (most recent call last):
|
||||
File "...sample_doctest_errors.py", line 5, in test.test_doctest.sample_doctest_errors
|
||||
>...>> 2 + 2
|
||||
AssertionError: Failed example:
|
||||
2 + 2
|
||||
Expected:
|
||||
5
|
||||
Got:
|
||||
4
|
||||
----------------------------------------------------------------------
|
||||
File "...sample_doctest_errors.py", line 7, in test.test_doctest.sample_doctest_errors
|
||||
Failed example:
|
||||
1/0
|
||||
Exception raised:
|
||||
Traceback (most recent call last):
|
||||
File "<doctest test.test_doctest.sample_doctest_errors[1]>", line 1, in <module>
|
||||
1/0
|
||||
~^~
|
||||
ZeroDivisionError: division by zero
|
||||
<BLANKLINE>
|
||||
>>> print(result.failures[1][1]) # doctest: +ELLIPSIS
|
||||
AssertionError: Failed doctest test for test.test_doctest.sample_doctest_errors.__test__.bad
|
||||
File "...sample_doctest_errors.py", line unknown line number, in bad
|
||||
<BLANKLINE>
|
||||
----------------------------------------------------------------------
|
||||
File "...sample_doctest_errors.py", line ?, in test.test_doctest.sample_doctest_errors.__test__.bad
|
||||
Failed example:
|
||||
Traceback (most recent call last):
|
||||
File "...sample_doctest_errors.py", line 37, in test.test_doctest.sample_doctest_errors.__test__.bad
|
||||
>...>> 2 + 2
|
||||
AssertionError: Failed example:
|
||||
2 + 2
|
||||
Expected:
|
||||
5
|
||||
Got:
|
||||
4
|
||||
----------------------------------------------------------------------
|
||||
File "...sample_doctest_errors.py", line ?, in test.test_doctest.sample_doctest_errors.__test__.bad
|
||||
Failed example:
|
||||
1/0
|
||||
Exception raised:
|
||||
Traceback (most recent call last):
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.__test__.bad[1]>", line 1, in <module>
|
||||
1/0
|
||||
~^~
|
||||
ZeroDivisionError: division by zero
|
||||
<BLANKLINE>
|
||||
>>> print(result.failures[2][1]) # doctest: +ELLIPSIS
|
||||
AssertionError: Failed doctest test for test.test_doctest.sample_doctest_errors.errors
|
||||
File "...sample_doctest_errors.py", line 14, in errors
|
||||
<BLANKLINE>
|
||||
----------------------------------------------------------------------
|
||||
File "...sample_doctest_errors.py", line 16, in test.test_doctest.sample_doctest_errors.errors
|
||||
Failed example:
|
||||
Traceback (most recent call last):
|
||||
File "...sample_doctest_errors.py", line 16, in test.test_doctest.sample_doctest_errors.errors
|
||||
>...>> 2 + 2
|
||||
AssertionError: Failed example:
|
||||
2 + 2
|
||||
Expected:
|
||||
5
|
||||
Got:
|
||||
4
|
||||
----------------------------------------------------------------------
|
||||
File "...sample_doctest_errors.py", line 18, in test.test_doctest.sample_doctest_errors.errors
|
||||
Failed example:
|
||||
<BLANKLINE>
|
||||
>>> print(result.errors[0][1]) # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
File "...sample_doctest_errors.py", line 7, in test.test_doctest.sample_doctest_errors
|
||||
>...>> 1/0
|
||||
File "<doctest test.test_doctest.sample_doctest_errors[1]>", line 1, in <module>
|
||||
1/0
|
||||
Exception raised:
|
||||
Traceback (most recent call last):
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.errors[1]>", line 1, in <module>
|
||||
1/0
|
||||
~^~
|
||||
ZeroDivisionError: division by zero
|
||||
----------------------------------------------------------------------
|
||||
File "...sample_doctest_errors.py", line 23, in test.test_doctest.sample_doctest_errors.errors
|
||||
Failed example:
|
||||
~^~
|
||||
ZeroDivisionError: division by zero
|
||||
<BLANKLINE>
|
||||
>>> print(result.errors[1][1]) # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
File "...sample_doctest_errors.py", line 39, in test.test_doctest.sample_doctest_errors.__test__.bad
|
||||
>...>> 1/0
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.__test__.bad[1]>", line 1, in <module>
|
||||
1/0
|
||||
~^~
|
||||
ZeroDivisionError: division by zero
|
||||
<BLANKLINE>
|
||||
>>> print(result.errors[2][1]) # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
File "...sample_doctest_errors.py", line 18, in test.test_doctest.sample_doctest_errors.errors
|
||||
>...>> 1/0
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.errors[1]>", line 1, in <module>
|
||||
1/0
|
||||
~^~
|
||||
ZeroDivisionError: division by zero
|
||||
<BLANKLINE>
|
||||
>>> print(result.errors[3][1]) # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
File "...sample_doctest_errors.py", line 23, in test.test_doctest.sample_doctest_errors.errors
|
||||
>...>> f()
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.errors[3]>", line 1, in <module>
|
||||
f()
|
||||
Exception raised:
|
||||
Traceback (most recent call last):
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.errors[3]>", line 1, in <module>
|
||||
f()
|
||||
~^^
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.errors[2]>", line 2, in f
|
||||
2 + '2'
|
||||
~~^~~~~
|
||||
TypeError: ...
|
||||
----------------------------------------------------------------------
|
||||
File "...sample_doctest_errors.py", line 25, in test.test_doctest.sample_doctest_errors.errors
|
||||
Failed example:
|
||||
~^^
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.errors[2]>", line 2, in f
|
||||
2 + '2'
|
||||
~~^~~~~
|
||||
TypeError: ...
|
||||
<BLANKLINE>
|
||||
>>> print(result.errors[4][1]) # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
File "...sample_doctest_errors.py", line 25, in test.test_doctest.sample_doctest_errors.errors
|
||||
>...>> g()
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.errors[4]>", line 1, in <module>
|
||||
g()
|
||||
Exception raised:
|
||||
Traceback (most recent call last):
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.errors[4]>", line 1, in <module>
|
||||
g()
|
||||
~^^
|
||||
File "...sample_doctest_errors.py", line 12, in g
|
||||
[][0] # line 12
|
||||
~~^^^
|
||||
IndexError: list index out of range
|
||||
~^^
|
||||
File "...sample_doctest_errors.py", line 12, in g
|
||||
[][0] # line 12
|
||||
~~^^^
|
||||
IndexError: list index out of range
|
||||
<BLANKLINE>
|
||||
>>> print(result.failures[3][1]) # doctest: +ELLIPSIS
|
||||
AssertionError: Failed doctest test for test.test_doctest.sample_doctest_errors.syntax_error
|
||||
File "...sample_doctest_errors.py", line 29, in syntax_error
|
||||
<BLANKLINE>
|
||||
----------------------------------------------------------------------
|
||||
File "...sample_doctest_errors.py", line 31, in test.test_doctest.sample_doctest_errors.syntax_error
|
||||
Failed example:
|
||||
>>> print(result.errors[5][1]) # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
File "...sample_doctest_errors.py", line 31, in test.test_doctest.sample_doctest_errors.syntax_error
|
||||
>...>> 2+*3
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.syntax_error[0]>", line 1
|
||||
2+*3
|
||||
Exception raised:
|
||||
File "<doctest test.test_doctest.sample_doctest_errors.syntax_error[0]>", line 1
|
||||
2+*3
|
||||
^
|
||||
SyntaxError: invalid syntax
|
||||
^
|
||||
SyntaxError: invalid syntax
|
||||
<BLANKLINE>
|
||||
"""
|
||||
|
||||
@@ -2536,7 +2639,7 @@ def test_DocFileSuite():
|
||||
... 'test_doctest2.txt',
|
||||
... 'test_doctest4.txt')
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=3 errors=0 failures=2>
|
||||
<unittest.result.TestResult run=3 errors=2 failures=0>
|
||||
|
||||
The test files are looked for in the directory containing the
|
||||
calling module. A package keyword argument can be provided to
|
||||
@@ -2548,14 +2651,14 @@ def test_DocFileSuite():
|
||||
... 'test_doctest4.txt',
|
||||
... package='test.test_doctest')
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=3 errors=0 failures=2>
|
||||
<unittest.result.TestResult run=3 errors=2 failures=0>
|
||||
|
||||
'/' should be used as a path separator. It will be converted
|
||||
to a native separator at run time:
|
||||
|
||||
>>> suite = doctest.DocFileSuite('../test_doctest/test_doctest.txt')
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=1 errors=0 failures=1>
|
||||
<unittest.result.TestResult run=1 errors=1 failures=0>
|
||||
|
||||
If DocFileSuite is used from an interactive session, then files
|
||||
are resolved relative to the directory of sys.argv[0]:
|
||||
@@ -2581,7 +2684,7 @@ def test_DocFileSuite():
|
||||
|
||||
>>> suite = doctest.DocFileSuite(test_file, module_relative=False)
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=1 errors=0 failures=1>
|
||||
<unittest.result.TestResult run=1 errors=1 failures=0>
|
||||
|
||||
It is an error to specify `package` when `module_relative=False`:
|
||||
|
||||
@@ -2599,12 +2702,15 @@ def test_DocFileSuite():
|
||||
... 'test_doctest_skip2.txt')
|
||||
>>> result = suite.run(unittest.TestResult())
|
||||
>>> result
|
||||
<unittest.result.TestResult run=4 errors=0 failures=1>
|
||||
<unittest.result.TestResult run=4 errors=1 failures=0>
|
||||
>>> len(result.skipped)
|
||||
1
|
||||
4
|
||||
>>> for tst, _ in result.skipped: # doctest: +ELLIPSIS
|
||||
... print('=', tst)
|
||||
= ...test_doctest_skip.txt [0]
|
||||
= ...test_doctest_skip.txt [1]
|
||||
= ...test_doctest_skip.txt
|
||||
= ...test_doctest_skip2.txt [0]
|
||||
|
||||
You can specify initial global variables:
|
||||
|
||||
@@ -2613,7 +2719,7 @@ def test_DocFileSuite():
|
||||
... 'test_doctest4.txt',
|
||||
... globs={'favorite_color': 'blue'})
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=3 errors=0 failures=1>
|
||||
<unittest.result.TestResult run=3 errors=1 failures=0>
|
||||
|
||||
In this case, we supplied a missing favorite color. You can
|
||||
provide doctest options:
|
||||
@@ -2624,7 +2730,7 @@ def test_DocFileSuite():
|
||||
... optionflags=doctest.DONT_ACCEPT_BLANKLINE,
|
||||
... globs={'favorite_color': 'blue'})
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=3 errors=0 failures=2>
|
||||
<unittest.result.TestResult run=3 errors=1 failures=1>
|
||||
|
||||
And, you can provide setUp and tearDown functions:
|
||||
|
||||
@@ -2643,7 +2749,7 @@ def test_DocFileSuite():
|
||||
... 'test_doctest4.txt',
|
||||
... setUp=setUp, tearDown=tearDown)
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=3 errors=0 failures=1>
|
||||
<unittest.result.TestResult run=3 errors=1 failures=0>
|
||||
|
||||
But the tearDown restores sanity:
|
||||
|
||||
@@ -2685,7 +2791,7 @@ def test_DocFileSuite():
|
||||
... 'test_doctest4.txt',
|
||||
... encoding='utf-8')
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=3 errors=0 failures=2>
|
||||
<unittest.result.TestResult run=3 errors=2 failures=0>
|
||||
"""
|
||||
|
||||
def test_DocFileSuite_errors():
|
||||
@@ -2695,52 +2801,49 @@ def test_DocFileSuite_errors():
|
||||
>>> suite = doctest.DocFileSuite('test_doctest_errors.txt')
|
||||
>>> result = suite.run(unittest.TestResult())
|
||||
>>> result
|
||||
<unittest.result.TestResult run=1 errors=0 failures=1>
|
||||
<unittest.result.TestResult run=1 errors=3 failures=1>
|
||||
>>> print(result.failures[0][1]) # doctest: +ELLIPSIS
|
||||
AssertionError: Failed doctest test for test_doctest_errors.txt
|
||||
File "...test_doctest_errors.txt", line 0
|
||||
<BLANKLINE>
|
||||
----------------------------------------------------------------------
|
||||
File "...test_doctest_errors.txt", line 4, in test_doctest_errors.txt
|
||||
Failed example:
|
||||
Traceback (most recent call last):
|
||||
File "...test_doctest_errors.txt", line 4, in test_doctest_errors.txt
|
||||
>...>> 2 + 2
|
||||
AssertionError: Failed example:
|
||||
2 + 2
|
||||
Expected:
|
||||
5
|
||||
Got:
|
||||
4
|
||||
----------------------------------------------------------------------
|
||||
File "...test_doctest_errors.txt", line 6, in test_doctest_errors.txt
|
||||
Failed example:
|
||||
1/0
|
||||
Exception raised:
|
||||
Traceback (most recent call last):
|
||||
File "<doctest test_doctest_errors.txt[1]>", line 1, in <module>
|
||||
1/0
|
||||
~^~
|
||||
ZeroDivisionError: division by zero
|
||||
----------------------------------------------------------------------
|
||||
File "...test_doctest_errors.txt", line 11, in test_doctest_errors.txt
|
||||
Failed example:
|
||||
f()
|
||||
Exception raised:
|
||||
Traceback (most recent call last):
|
||||
File "<doctest test_doctest_errors.txt[3]>", line 1, in <module>
|
||||
f()
|
||||
~^^
|
||||
File "<doctest test_doctest_errors.txt[2]>", line 2, in f
|
||||
2 + '2'
|
||||
~~^~~~~
|
||||
TypeError: ...
|
||||
----------------------------------------------------------------------
|
||||
File "...test_doctest_errors.txt", line 13, in test_doctest_errors.txt
|
||||
Failed example:
|
||||
2+*3
|
||||
Exception raised:
|
||||
File "<doctest test_doctest_errors.txt[4]>", line 1
|
||||
2+*3
|
||||
^
|
||||
SyntaxError: invalid syntax
|
||||
<BLANKLINE>
|
||||
>>> print(result.errors[0][1]) # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
File "...test_doctest_errors.txt", line 6, in test_doctest_errors.txt
|
||||
>...>> 1/0
|
||||
File "<doctest test_doctest_errors.txt[1]>", line 1, in <module>
|
||||
1/0
|
||||
~^~
|
||||
ZeroDivisionError: division by zero
|
||||
<BLANKLINE>
|
||||
>>> print(result.errors[1][1]) # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
File "...test_doctest_errors.txt", line 11, in test_doctest_errors.txt
|
||||
>...>> f()
|
||||
File "<doctest test_doctest_errors.txt[3]>", line 1, in <module>
|
||||
f()
|
||||
~^^
|
||||
File "<doctest test_doctest_errors.txt[2]>", line 2, in f
|
||||
2 + '2'
|
||||
~~^~~~~
|
||||
TypeError: ...
|
||||
<BLANKLINE>
|
||||
>>> print(result.errors[2][1]) # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
File "...test_doctest_errors.txt", line 13, in test_doctest_errors.txt
|
||||
>...>> 2+*3
|
||||
File "<doctest test_doctest_errors.txt[4]>", line 1
|
||||
2+*3
|
||||
^
|
||||
SyntaxError: invalid syntax
|
||||
<BLANKLINE>
|
||||
|
||||
"""
|
||||
|
||||
def test_trailing_space_in_test():
|
||||
@@ -2811,16 +2914,25 @@ def test_unittest_reportflags():
|
||||
>>> import unittest
|
||||
>>> result = suite.run(unittest.TestResult())
|
||||
>>> result
|
||||
<unittest.result.TestResult run=1 errors=0 failures=1>
|
||||
<unittest.result.TestResult run=1 errors=1 failures=1>
|
||||
>>> print(result.failures[0][1]) # doctest: +ELLIPSIS
|
||||
AssertionError: Failed doctest test for test_doctest.txt
|
||||
...
|
||||
Failed example:
|
||||
favorite_color
|
||||
...
|
||||
Failed example:
|
||||
Traceback (most recent call last):
|
||||
File ...
|
||||
>...>> if 1:
|
||||
AssertionError: Failed example:
|
||||
if 1:
|
||||
...
|
||||
print('a')
|
||||
print()
|
||||
print('b')
|
||||
Expected:
|
||||
a
|
||||
<BLANKLINE>
|
||||
b
|
||||
Got:
|
||||
a
|
||||
<BLANKLINE>
|
||||
b
|
||||
<BLANKLINE>
|
||||
|
||||
Note that we see both failures displayed.
|
||||
|
||||
@@ -2829,18 +2941,8 @@ def test_unittest_reportflags():
|
||||
|
||||
Now, when we run the test:
|
||||
|
||||
>>> result = suite.run(unittest.TestResult())
|
||||
>>> result
|
||||
<unittest.result.TestResult run=1 errors=0 failures=1>
|
||||
>>> print(result.failures[0][1]) # doctest: +ELLIPSIS
|
||||
AssertionError: Failed doctest test for test_doctest.txt
|
||||
...
|
||||
Failed example:
|
||||
favorite_color
|
||||
Exception raised:
|
||||
...
|
||||
NameError: name 'favorite_color' is not defined
|
||||
<BLANKLINE>
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=1 errors=1 failures=0>
|
||||
|
||||
We get only the first failure.
|
||||
|
||||
@@ -2850,22 +2952,20 @@ def test_unittest_reportflags():
|
||||
>>> suite = doctest.DocFileSuite('test_doctest.txt',
|
||||
... optionflags=doctest.DONT_ACCEPT_BLANKLINE | doctest.REPORT_NDIFF)
|
||||
|
||||
Then the default eporting options are ignored:
|
||||
Then the default reporting options are ignored:
|
||||
|
||||
>>> result = suite.run(unittest.TestResult())
|
||||
>>> result
|
||||
<unittest.result.TestResult run=1 errors=0 failures=1>
|
||||
<unittest.result.TestResult run=1 errors=1 failures=1>
|
||||
|
||||
*NOTE*: These doctest are intentionally not placed in raw string to depict
|
||||
the trailing whitespace using `\x20` in the diff below.
|
||||
|
||||
>>> print(result.failures[0][1]) # doctest: +ELLIPSIS
|
||||
AssertionError: Failed doctest test for test_doctest.txt
|
||||
...
|
||||
Failed example:
|
||||
favorite_color
|
||||
...
|
||||
Failed example:
|
||||
Traceback ...
|
||||
File ...
|
||||
>...>> if 1:
|
||||
AssertionError: Failed example:
|
||||
if 1:
|
||||
print('a')
|
||||
print()
|
||||
@@ -3270,7 +3370,7 @@ Tests for error reporting in the testmod() function.
|
||||
~^~
|
||||
ZeroDivisionError: division by zero
|
||||
**********************************************************************
|
||||
File "...sample_doctest_errors.py", line ?, in test.test_doctest.sample_doctest_errors.__test__.bad
|
||||
File "...sample_doctest_errors.py", line 37, in test.test_doctest.sample_doctest_errors.__test__.bad
|
||||
Failed example:
|
||||
2 + 2
|
||||
Expected:
|
||||
@@ -3278,7 +3378,7 @@ Tests for error reporting in the testmod() function.
|
||||
Got:
|
||||
4
|
||||
**********************************************************************
|
||||
File "...sample_doctest_errors.py", line ?, in test.test_doctest.sample_doctest_errors.__test__.bad
|
||||
File "...sample_doctest_errors.py", line 39, in test.test_doctest.sample_doctest_errors.__test__.bad
|
||||
Failed example:
|
||||
1/0
|
||||
Exception raised:
|
||||
@@ -3673,9 +3773,9 @@ def test_run_doctestsuite_multiple_times():
|
||||
>>> import test.test_doctest.sample_doctest
|
||||
>>> suite = doctest.DocTestSuite(test.test_doctest.sample_doctest)
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=9 errors=0 failures=4>
|
||||
<unittest.result.TestResult run=9 errors=2 failures=2>
|
||||
>>> suite.run(unittest.TestResult())
|
||||
<unittest.result.TestResult run=9 errors=0 failures=4>
|
||||
<unittest.result.TestResult run=9 errors=2 failures=2>
|
||||
"""
|
||||
|
||||
|
||||
@@ -3855,8 +3955,7 @@ def test_syntax_error_with_incorrect_expected_note():
|
||||
|
||||
def load_tests(loader, tests, pattern):
|
||||
tests.addTest(doctest.DocTestSuite(doctest))
|
||||
from test.support.rustpython import DocTestChecker # TODO: RUSTPYTHON
|
||||
tests.addTest(doctest.DocTestSuite(checker=DocTestChecker())) # TODO: RUSTPYTHON
|
||||
tests.addTest(doctest.DocTestSuite())
|
||||
return tests
|
||||
|
||||
|
||||
|
||||
Vendored
+12
-1
@@ -74,6 +74,9 @@ class OrderedDictTests:
|
||||
od.update(dict(pairs))
|
||||
self.assertEqual(sorted(od.items()), pairs) # dict input
|
||||
od = OrderedDict()
|
||||
od.update(frozendict(pairs))
|
||||
self.assertEqual(sorted(od.items()), pairs) # frozendict input
|
||||
od = OrderedDict()
|
||||
od.update(**dict(pairs))
|
||||
self.assertEqual(sorted(od.items()), pairs) # kwds input
|
||||
od = OrderedDict()
|
||||
@@ -288,9 +291,11 @@ class OrderedDictTests:
|
||||
pairs = pairs[2:] + pairs[:2]
|
||||
od2 = OrderedDict(pairs)
|
||||
self.assertNotEqual(od1, od2) # different order implies inequality
|
||||
# comparison to regular dict is not order sensitive
|
||||
# comparison to regular (frozen)dict is not order sensitive
|
||||
self.assertEqual(od1, dict(od2))
|
||||
self.assertEqual(dict(od2), od1)
|
||||
self.assertEqual(od1, frozendict(od2))
|
||||
self.assertEqual(frozendict(od1), od2)
|
||||
# different length implied inequality
|
||||
self.assertNotEqual(od1, OrderedDict(pairs[:-1]))
|
||||
|
||||
@@ -698,6 +703,7 @@ class OrderedDictTests:
|
||||
d |= list(b.items())
|
||||
expected = OrderedDict({0: 0, 1: 1, 2: 2, 3: 3})
|
||||
self.assertEqual(a | dict(b), expected)
|
||||
self.assertEqual(a | frozendict(b), expected)
|
||||
self.assertEqual(a | b, expected)
|
||||
self.assertEqual(c, expected)
|
||||
self.assertEqual(d, expected)
|
||||
@@ -706,12 +712,17 @@ class OrderedDictTests:
|
||||
c |= a
|
||||
expected = OrderedDict({1: 1, 2: 1, 3: 3, 0: 0})
|
||||
self.assertEqual(dict(b) | a, expected)
|
||||
self.assertEqual(frozendict(b) | a, expected)
|
||||
self.assertEqual(a.__ror__(frozendict(b)), expected)
|
||||
self.assertEqual(b | a, expected)
|
||||
self.assertEqual(c, expected)
|
||||
|
||||
self.assertIs(type(a | b), OrderedDict)
|
||||
self.assertIs(type(dict(a) | b), OrderedDict)
|
||||
self.assertIs(type(frozendict(a) | b), frozendict)
|
||||
self.assertIs(type(b.__ror__(frozendict(a))), OrderedDict)
|
||||
self.assertIs(type(a | dict(b)), OrderedDict)
|
||||
self.assertIs(type(a | frozendict(b)), OrderedDict)
|
||||
|
||||
expected = a.copy()
|
||||
a |= ()
|
||||
|
||||
Vendored
+26
-15
@@ -166,21 +166,20 @@ class ParseArgsTestCase(unittest.TestCase):
|
||||
ns = self.parse_args([opt])
|
||||
self.assertTrue(ns.randomize)
|
||||
|
||||
with os_helper.EnvironmentVarGuard() as env:
|
||||
# with SOURCE_DATE_EPOCH
|
||||
env['SOURCE_DATE_EPOCH'] = '1697839080'
|
||||
ns = self.parse_args(['--randomize'])
|
||||
regrtest = main.Regrtest(ns)
|
||||
self.assertFalse(regrtest.randomize)
|
||||
self.assertIsInstance(regrtest.random_seed, str)
|
||||
self.assertEqual(regrtest.random_seed, '1697839080')
|
||||
@os_helper.with_source_date_epoch(epoch=1697839080)
|
||||
def test_randomize_with_source_date_epoch(self):
|
||||
ns = self.parse_args(['--randomize'])
|
||||
regrtest = main.Regrtest(ns)
|
||||
self.assertFalse(regrtest.randomize)
|
||||
self.assertIsInstance(regrtest.random_seed, str)
|
||||
self.assertEqual(regrtest.random_seed, '1697839080')
|
||||
|
||||
# without SOURCE_DATE_EPOCH
|
||||
del env['SOURCE_DATE_EPOCH']
|
||||
ns = self.parse_args(['--randomize'])
|
||||
regrtest = main.Regrtest(ns)
|
||||
self.assertTrue(regrtest.randomize)
|
||||
self.assertIsInstance(regrtest.random_seed, int)
|
||||
@os_helper.without_source_date_epoch
|
||||
def test_randomize_without_source_date_epoch(self):
|
||||
ns = self.parse_args(['--randomize'])
|
||||
regrtest = main.Regrtest(ns)
|
||||
self.assertTrue(regrtest.randomize)
|
||||
self.assertIsInstance(regrtest.random_seed, int)
|
||||
|
||||
def test_no_randomize(self):
|
||||
ns = self.parse_args([])
|
||||
@@ -571,6 +570,13 @@ class ParseArgsTestCase(unittest.TestCase):
|
||||
self.assertEqual(regrtest.num_workers, 0)
|
||||
self.assertTrue(regrtest.single_process)
|
||||
|
||||
def test_pythoninfo(self):
|
||||
ns = self.parse_args([])
|
||||
self.assertFalse(ns.pythoninfo)
|
||||
|
||||
ns = self.parse_args(['--pythoninfo'])
|
||||
self.assertTrue(ns.pythoninfo)
|
||||
|
||||
|
||||
@dataclasses.dataclass(slots=True)
|
||||
class Rerun:
|
||||
@@ -2141,7 +2147,7 @@ class ArgsTestCase(BaseTestCase):
|
||||
self.check_executed_tests(output, [testname],
|
||||
failed=[testname],
|
||||
parallel=True,
|
||||
stats=TestStats(1, 1, 0))
|
||||
stats=TestStats(1, 2, 1))
|
||||
|
||||
def _check_random_seed(self, run_workers: bool):
|
||||
# gh-109276: When -r/--randomize is used, random.seed() is called
|
||||
@@ -2428,6 +2434,11 @@ class ArgsTestCase(BaseTestCase):
|
||||
self.assertNotIn('test_re', tests)
|
||||
self.assertEqual(len(tests), len(pgo_tests) - 1)
|
||||
|
||||
def test_pythoninfo(self):
|
||||
testname = self.create_test()
|
||||
output = self.run_tests('--pythoninfo', testname)
|
||||
self.assertIn("Python build information", output)
|
||||
|
||||
|
||||
class TestUtils(unittest.TestCase):
|
||||
def test_format_duration(self):
|
||||
|
||||
+6
-5
@@ -406,11 +406,12 @@ class TestLongMessage(unittest.TestCase):
|
||||
# test warning raised but with wrong message
|
||||
def raise_wrong_message():
|
||||
warnings.warn('foo')
|
||||
self.assertMessagesCM('assertWarnsRegex', (UserWarning, 'regex'),
|
||||
raise_wrong_message,
|
||||
['^"regex" does not match "foo"$', '^oops$',
|
||||
'^"regex" does not match "foo"$',
|
||||
'^"regex" does not match "foo" : oops$'])
|
||||
with self.assertWarnsRegex(UserWarning, 'foo'):
|
||||
self.assertMessagesCM('assertWarnsRegex', (UserWarning, 'regex'),
|
||||
raise_wrong_message,
|
||||
['^"regex" does not match "foo"$', '^oops$',
|
||||
'^"regex" does not match "foo"$',
|
||||
'^"regex" does not match "foo" : oops$'])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Vendored
+125
-19
@@ -1631,11 +1631,11 @@ test case
|
||||
self.assertRaisesRegex((ValueError, object), 'expect')
|
||||
|
||||
def testAssertWarnsCallable(self):
|
||||
def _runtime_warn():
|
||||
warnings.warn("foo", RuntimeWarning)
|
||||
def _runtime_warn(categories=(RuntimeWarning,)):
|
||||
for category in categories:
|
||||
warnings.warn("foo", category)
|
||||
# Success when the right warning is triggered, even several times
|
||||
self.assertWarns(RuntimeWarning, _runtime_warn)
|
||||
self.assertWarns(RuntimeWarning, _runtime_warn)
|
||||
self.assertWarns(RuntimeWarning, _runtime_warn, (RuntimeWarning, RuntimeWarning))
|
||||
# A tuple of warning classes is accepted
|
||||
self.assertWarns((DeprecationWarning, RuntimeWarning), _runtime_warn)
|
||||
# *args and **kwargs also work
|
||||
@@ -1648,22 +1648,35 @@ test case
|
||||
with self.assertRaises(TypeError):
|
||||
self.assertWarns(RuntimeWarning, None)
|
||||
# Failure when another warning is triggered
|
||||
with warnings.catch_warnings():
|
||||
with warnings.catch_warnings(record=True) as log:
|
||||
# Force default filter (in case tests are run with -We)
|
||||
warnings.simplefilter("default", RuntimeWarning)
|
||||
with self.assertRaises(self.failureException):
|
||||
self.assertWarns(DeprecationWarning, _runtime_warn)
|
||||
self.assertWarns(DeprecationWarning, _runtime_warn,
|
||||
(RuntimeWarning, RuntimeWarning))
|
||||
self.assertEqual(len(log), 1, log)
|
||||
self.assertIsInstance(log[0].message, RuntimeWarning)
|
||||
# Filters for other warnings are not modified
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error", RuntimeWarning)
|
||||
with self.assertRaises(RuntimeWarning):
|
||||
self.assertWarns(DeprecationWarning, _runtime_warn)
|
||||
# Warnings that do not match the category are not swallowed.
|
||||
with self.assertWarns(RuntimeWarning):
|
||||
with self.assertRaises(self.failureException):
|
||||
self.assertWarns(DeprecationWarning, _runtime_warn)
|
||||
with self.assertWarns(RuntimeWarning):
|
||||
self.assertWarns(DeprecationWarning, _runtime_warn,
|
||||
(RuntimeWarning, DeprecationWarning))
|
||||
with self.assertWarns(RuntimeWarning):
|
||||
self.assertWarns(DeprecationWarning, _runtime_warn,
|
||||
(DeprecationWarning, RuntimeWarning))
|
||||
|
||||
def testAssertWarnsContext(self):
|
||||
# Believe it or not, it is preferable to duplicate all tests above,
|
||||
# to make sure the __warningregistry__ $@ is circumvented correctly.
|
||||
def _runtime_warn():
|
||||
warnings.warn("foo", RuntimeWarning)
|
||||
def _runtime_warn(category=RuntimeWarning):
|
||||
warnings.warn("foo", category)
|
||||
_runtime_warn_lineno = inspect.getsourcelines(_runtime_warn)[1]
|
||||
with self.assertWarns(RuntimeWarning) as cm:
|
||||
_runtime_warn()
|
||||
@@ -1694,18 +1707,58 @@ test case
|
||||
with self.assertWarns(RuntimeWarning, foobar=42):
|
||||
pass
|
||||
# Failure when another warning is triggered
|
||||
with warnings.catch_warnings():
|
||||
with warnings.catch_warnings(record=True) as log:
|
||||
# Force default filter (in case tests are run with -We)
|
||||
warnings.simplefilter("default", RuntimeWarning)
|
||||
with self.assertRaises(self.failureException):
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
_runtime_warn()
|
||||
_runtime_warn()
|
||||
self.assertEqual(len(log), 1, log)
|
||||
self.assertIsInstance(log[0].message, RuntimeWarning)
|
||||
with warnings.catch_warnings(record=True) as log:
|
||||
# Force default filter (in case tests are run with -We)
|
||||
warnings.simplefilter("error", RuntimeWarning)
|
||||
warnings.filterwarnings("default", category=RuntimeWarning,
|
||||
module=__name__)
|
||||
with self.assertRaises(self.failureException):
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
_runtime_warn()
|
||||
_runtime_warn()
|
||||
self.assertEqual(len(log), 1, log)
|
||||
self.assertIsInstance(log[0].message, RuntimeWarning)
|
||||
# Filters for other warnings are not modified
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error", RuntimeWarning)
|
||||
with self.assertRaises(RuntimeWarning):
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
_runtime_warn()
|
||||
# Warnings that do not match the category are not swallowed.
|
||||
with self.assertWarns(RuntimeWarning):
|
||||
with self.assertRaises(self.failureException):
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
_runtime_warn()
|
||||
with self.assertWarns(RuntimeWarning):
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
_runtime_warn()
|
||||
_runtime_warn(DeprecationWarning)
|
||||
with self.assertWarns(RuntimeWarning):
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
_runtime_warn(DeprecationWarning)
|
||||
_runtime_warn()
|
||||
# Filters by module name work for other warnings.
|
||||
with warnings.catch_warnings(record=True) as log:
|
||||
warnings.filterwarnings("error", category=RuntimeWarning)
|
||||
warnings.filterwarnings("default", category=RuntimeWarning,
|
||||
module=re.escape(__name__))
|
||||
warnings.filterwarnings("error", category=RuntimeWarning,
|
||||
module='test_case')
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
_runtime_warn(DeprecationWarning)
|
||||
_runtime_warn()
|
||||
_runtime_warn()
|
||||
self.assertEqual(len(log), 1, log)
|
||||
self.assertIsInstance(log[0].message, RuntimeWarning)
|
||||
|
||||
def testAssertWarnsNoExceptionType(self):
|
||||
with self.assertRaises(TypeError):
|
||||
@@ -1722,8 +1775,9 @@ test case
|
||||
self.assertWarns((UserWarning, Exception))
|
||||
|
||||
def testAssertWarnsRegexCallable(self):
|
||||
def _runtime_warn(msg):
|
||||
warnings.warn(msg, RuntimeWarning)
|
||||
def _runtime_warn(*msgs):
|
||||
for msg in msgs:
|
||||
warnings.warn(msg, RuntimeWarning)
|
||||
self.assertWarnsRegex(RuntimeWarning, "o+",
|
||||
_runtime_warn, "foox")
|
||||
# Failure when no warning is triggered
|
||||
@@ -1734,16 +1788,26 @@ test case
|
||||
with self.assertRaises(TypeError):
|
||||
self.assertWarnsRegex(RuntimeWarning, "o+", None)
|
||||
# Failure when another warning is triggered
|
||||
with warnings.catch_warnings():
|
||||
with warnings.catch_warnings(record=True) as log:
|
||||
# Force default filter (in case tests are run with -We)
|
||||
warnings.simplefilter("default", RuntimeWarning)
|
||||
with self.assertRaises(self.failureException):
|
||||
self.assertWarnsRegex(DeprecationWarning, "o+",
|
||||
_runtime_warn, "foox")
|
||||
# Failure when message doesn't match
|
||||
with self.assertRaises(self.failureException):
|
||||
_runtime_warn, "foox", "foox")
|
||||
self.assertEqual(len(log), 1, log)
|
||||
self.assertIsInstance(log[0].message, RuntimeWarning)
|
||||
# Failure when message doesn't match.
|
||||
# Warnings that do not match the regex are not swallowed.
|
||||
with self.assertWarnsRegex(RuntimeWarning, "ar"):
|
||||
with self.assertRaises(self.failureException):
|
||||
self.assertWarnsRegex(RuntimeWarning, "o+",
|
||||
_runtime_warn, "barz")
|
||||
with self.assertWarnsRegex(RuntimeWarning, "ar"):
|
||||
self.assertWarnsRegex(RuntimeWarning, "o+",
|
||||
_runtime_warn, "barz")
|
||||
_runtime_warn, "barz", "foox")
|
||||
with self.assertWarnsRegex(RuntimeWarning, "ar"):
|
||||
self.assertWarnsRegex(RuntimeWarning, "o+",
|
||||
_runtime_warn, "foox", "barz")
|
||||
# A little trickier: we ask RuntimeWarnings to be raised, and then
|
||||
# check for some of them. It is implementation-defined whether
|
||||
# non-matching RuntimeWarnings are simply re-raised, or produce a
|
||||
@@ -1778,16 +1842,29 @@ test case
|
||||
with self.assertWarnsRegex(RuntimeWarning, 'o+', foobar=42):
|
||||
pass
|
||||
# Failure when another warning is triggered
|
||||
with warnings.catch_warnings():
|
||||
with warnings.catch_warnings(record=True) as log:
|
||||
# Force default filter (in case tests are run with -We)
|
||||
warnings.simplefilter("default", RuntimeWarning)
|
||||
with self.assertRaises(self.failureException):
|
||||
with self.assertWarnsRegex(DeprecationWarning, "o+"):
|
||||
_runtime_warn("foox")
|
||||
# Failure when message doesn't match
|
||||
with self.assertRaises(self.failureException):
|
||||
_runtime_warn("foox")
|
||||
self.assertEqual(len(log), 1, log)
|
||||
self.assertIsInstance(log[0].message, RuntimeWarning)
|
||||
# Failure when message doesn't match.
|
||||
# Warnings that do not match the regex are not swallowed.
|
||||
with self.assertWarnsRegex(RuntimeWarning, "ar"):
|
||||
with self.assertRaises(self.failureException):
|
||||
with self.assertWarnsRegex(RuntimeWarning, "o+"):
|
||||
_runtime_warn("barz")
|
||||
with self.assertWarnsRegex(RuntimeWarning, "ar"):
|
||||
with self.assertWarnsRegex(RuntimeWarning, "o+"):
|
||||
_runtime_warn("barz")
|
||||
_runtime_warn("foox")
|
||||
with self.assertWarnsRegex(RuntimeWarning, "ar"):
|
||||
with self.assertWarnsRegex(RuntimeWarning, "o+"):
|
||||
_runtime_warn("foox")
|
||||
_runtime_warn("barz")
|
||||
# A little trickier: we ask RuntimeWarnings to be raised, and then
|
||||
# check for some of them. It is implementation-defined whether
|
||||
# non-matching RuntimeWarnings are simply re-raised, or produce a
|
||||
@@ -1797,6 +1874,19 @@ test case
|
||||
with self.assertRaises((RuntimeWarning, self.failureException)):
|
||||
with self.assertWarnsRegex(RuntimeWarning, "o+"):
|
||||
_runtime_warn("barz")
|
||||
# Filters by module name work for warnings with other message.
|
||||
with warnings.catch_warnings(record=True) as log:
|
||||
warnings.filterwarnings("error", category=RuntimeWarning)
|
||||
warnings.filterwarnings("default", category=RuntimeWarning,
|
||||
module=re.escape(__name__))
|
||||
warnings.filterwarnings("error", category=RuntimeWarning,
|
||||
module='test_case')
|
||||
with self.assertWarnsRegex(RuntimeWarning, "ar"):
|
||||
_runtime_warn("bar")
|
||||
_runtime_warn("foox")
|
||||
_runtime_warn("foox")
|
||||
self.assertEqual(len(log), 1, log)
|
||||
self.assertIsInstance(log[0].message, RuntimeWarning)
|
||||
|
||||
def testAssertWarnsRegexNoExceptionType(self):
|
||||
with self.assertRaises(TypeError):
|
||||
@@ -1920,6 +2010,22 @@ test case
|
||||
with self.assertLogs():
|
||||
raise ZeroDivisionError("Unexpected")
|
||||
|
||||
def testAssertLogsWithFormatter(self):
|
||||
# Check alternative formats will be respected
|
||||
format = "[No.1: the larch] %(levelname)s:%(name)s:%(message)s"
|
||||
formatter = logging.Formatter(format)
|
||||
with self.assertNoStderr():
|
||||
with self.assertLogs() as cm:
|
||||
log_foo.info("1")
|
||||
log_foobar.debug("2")
|
||||
self.assertEqual(cm.output, ["INFO:foo:1"])
|
||||
self.assertLogRecords(cm.records, [{'name': 'foo'}])
|
||||
with self.assertLogs(formatter=formatter) as cm:
|
||||
log_foo.info("1")
|
||||
log_foobar.debug("2")
|
||||
self.assertEqual(cm.output, ["[No.1: the larch] INFO:foo:1"])
|
||||
self.assertLogRecords(cm.records, [{'name': 'foo'}])
|
||||
|
||||
def testAssertNoLogsDefault(self):
|
||||
with self.assertRaises(self.failureException) as cm:
|
||||
with self.assertNoLogs():
|
||||
|
||||
+51
@@ -1,3 +1,4 @@
|
||||
import abc
|
||||
import functools
|
||||
import sys
|
||||
import types
|
||||
@@ -98,6 +99,22 @@ class Test_TestLoader(unittest.TestCase):
|
||||
self.assertIsInstance(suite, loader.suiteClass)
|
||||
self.assertEqual(list(suite), [])
|
||||
|
||||
# "Do not load any tests from a TestCase-derived class that is an abstract
|
||||
# base class."
|
||||
def test_loadTestsFromTestCase__from_abc_TestCase(self):
|
||||
class FooBase(unittest.TestCase, metaclass=abc.ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def test(self): ...
|
||||
class Foo(FooBase):
|
||||
def test(self): pass
|
||||
|
||||
empty_suite = unittest.TestSuite()
|
||||
|
||||
loader = unittest.TestLoader()
|
||||
suite = loader.loadTestsFromTestCase(Foo)
|
||||
self.assertEqual(loader.loadTestsFromTestCase(FooBase), empty_suite)
|
||||
self.assertEqual(list(suite), [Foo('test')])
|
||||
|
||||
################################################################
|
||||
### /Tests for TestLoader.loadTestsFromTestCase
|
||||
|
||||
@@ -252,6 +269,24 @@ class Test_TestLoader(unittest.TestCase):
|
||||
|
||||
self.assertRaisesRegex(TypeError, "some failure", test.m)
|
||||
|
||||
# Check that loadTestsFromModule skips abstract base classes derived from
|
||||
# TestCase, which can't be instantiated.
|
||||
def test_loadTestsFromModule__skip_abc_TestCase(self):
|
||||
m = types.ModuleType('m')
|
||||
class MyTestCaseBase(unittest.TestCase, metaclass=abc.ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def test(self):
|
||||
...
|
||||
class MyTestCase(MyTestCaseBase):
|
||||
def test(self):
|
||||
pass
|
||||
m.testcase_1 = MyTestCaseBase
|
||||
m.testcase_2 = MyTestCase
|
||||
loader = unittest.TestLoader()
|
||||
suite = loader.loadTestsFromModule(m)
|
||||
expected = [loader.suiteClass([MyTestCase('test')])]
|
||||
self.assertEqual(list(suite), expected)
|
||||
|
||||
################################################################
|
||||
### /Tests for TestLoader.loadTestsFromModule()
|
||||
|
||||
@@ -1052,6 +1087,22 @@ class Test_TestLoader(unittest.TestCase):
|
||||
if module_name in sys.modules:
|
||||
del sys.modules[module_name]
|
||||
|
||||
# "The specifier should not refer to a test method in a TestCase-derived
|
||||
# subclass that is an abstract base class"
|
||||
def test_loadTestsFromNames__testmethod_in_abc_TestCase(self):
|
||||
m = types.ModuleType('m')
|
||||
class Foo(unittest.TestCase, metaclass=abc.ABCMeta):
|
||||
@abc.abstractmethod
|
||||
def test_1(self): ...
|
||||
def test_2(self): pass
|
||||
m.Foo = Foo
|
||||
|
||||
loader = unittest.TestLoader()
|
||||
for name in 'Foo.test_1', 'Foo.test_2':
|
||||
with self.subTest(name=name), self.assertRaisesRegex(TypeError,
|
||||
"Cannot instantiate abstract test case Foo"):
|
||||
loader.loadTestsFromNames([name], m)
|
||||
|
||||
################################################################
|
||||
### /Tests for TestLoader.loadTestsFromNames()
|
||||
|
||||
|
||||
+34
-3
@@ -1282,14 +1282,22 @@ class TestOutputBuffering(unittest.TestCase):
|
||||
suite(result)
|
||||
expected_out = '\nStdout:\ndo cleanup2\ndo cleanup1\n'
|
||||
self.assertEqual(stdout.getvalue(), expected_out)
|
||||
self.assertEqual(len(result.errors), 1)
|
||||
self.assertEqual(len(result.errors), 2)
|
||||
description = 'tearDownModule (Module)'
|
||||
test_case, formatted_exc = result.errors[0]
|
||||
self.assertEqual(test_case.description, description)
|
||||
self.assertIn('ValueError: bad cleanup2', formatted_exc)
|
||||
self.assertNotIn('ExceptionGroup', formatted_exc)
|
||||
self.assertNotIn('TypeError', formatted_exc)
|
||||
self.assertIn(expected_out, formatted_exc)
|
||||
|
||||
test_case, formatted_exc = result.errors[1]
|
||||
self.assertEqual(test_case.description, description)
|
||||
self.assertIn('TypeError: bad cleanup1', formatted_exc)
|
||||
self.assertNotIn('ExceptionGroup', formatted_exc)
|
||||
self.assertNotIn('ValueError', formatted_exc)
|
||||
self.assertIn(expected_out, formatted_exc)
|
||||
|
||||
def testBufferSetUpModule_DoModuleCleanups(self):
|
||||
with captured_stdout() as stdout:
|
||||
result = unittest.TestResult()
|
||||
@@ -1313,22 +1321,34 @@ class TestOutputBuffering(unittest.TestCase):
|
||||
suite(result)
|
||||
expected_out = '\nStdout:\nset up module\ndo cleanup2\ndo cleanup1\n'
|
||||
self.assertEqual(stdout.getvalue(), expected_out)
|
||||
self.assertEqual(len(result.errors), 2)
|
||||
self.assertEqual(len(result.errors), 3)
|
||||
description = 'setUpModule (Module)'
|
||||
test_case, formatted_exc = result.errors[0]
|
||||
self.assertEqual(test_case.description, description)
|
||||
self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
|
||||
self.assertNotIn('ExceptionGroup', formatted_exc)
|
||||
self.assertNotIn('ValueError', formatted_exc)
|
||||
self.assertNotIn('TypeError', formatted_exc)
|
||||
self.assertIn('\nStdout:\nset up module\n', formatted_exc)
|
||||
|
||||
test_case, formatted_exc = result.errors[1]
|
||||
self.assertIn(expected_out, formatted_exc)
|
||||
self.assertEqual(test_case.description, description)
|
||||
self.assertIn('ValueError: bad cleanup2', formatted_exc)
|
||||
self.assertNotIn('ExceptionGroup', formatted_exc)
|
||||
self.assertNotIn('ZeroDivisionError', formatted_exc)
|
||||
self.assertNotIn('TypeError', formatted_exc)
|
||||
self.assertIn(expected_out, formatted_exc)
|
||||
|
||||
test_case, formatted_exc = result.errors[2]
|
||||
self.assertIn(expected_out, formatted_exc)
|
||||
self.assertEqual(test_case.description, description)
|
||||
self.assertIn('TypeError: bad cleanup1', formatted_exc)
|
||||
self.assertNotIn('ExceptionGroup', formatted_exc)
|
||||
self.assertNotIn('ZeroDivisionError', formatted_exc)
|
||||
self.assertNotIn('ValueError', formatted_exc)
|
||||
self.assertIn(expected_out, formatted_exc)
|
||||
|
||||
def testBufferTearDownModule_DoModuleCleanups(self):
|
||||
with captured_stdout() as stdout:
|
||||
result = unittest.TestResult()
|
||||
@@ -1355,21 +1375,32 @@ class TestOutputBuffering(unittest.TestCase):
|
||||
suite(result)
|
||||
expected_out = '\nStdout:\ntear down module\ndo cleanup2\ndo cleanup1\n'
|
||||
self.assertEqual(stdout.getvalue(), expected_out)
|
||||
self.assertEqual(len(result.errors), 2)
|
||||
self.assertEqual(len(result.errors), 3)
|
||||
description = 'tearDownModule (Module)'
|
||||
test_case, formatted_exc = result.errors[0]
|
||||
self.assertEqual(test_case.description, description)
|
||||
self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
|
||||
self.assertNotIn('ExceptionGroup', formatted_exc)
|
||||
self.assertNotIn('ValueError', formatted_exc)
|
||||
self.assertNotIn('TypeError', formatted_exc)
|
||||
self.assertIn('\nStdout:\ntear down module\n', formatted_exc)
|
||||
|
||||
test_case, formatted_exc = result.errors[1]
|
||||
self.assertEqual(test_case.description, description)
|
||||
self.assertIn('ValueError: bad cleanup2', formatted_exc)
|
||||
self.assertNotIn('ExceptionGroup', formatted_exc)
|
||||
self.assertNotIn('ZeroDivisionError', formatted_exc)
|
||||
self.assertNotIn('TypeError', formatted_exc)
|
||||
self.assertIn(expected_out, formatted_exc)
|
||||
|
||||
test_case, formatted_exc = result.errors[2]
|
||||
self.assertEqual(test_case.description, description)
|
||||
self.assertIn('TypeError: bad cleanup1', formatted_exc)
|
||||
self.assertNotIn('ExceptionGroup', formatted_exc)
|
||||
self.assertNotIn('ZeroDivisionError', formatted_exc)
|
||||
self.assertNotIn('ValueError', formatted_exc)
|
||||
self.assertIn(expected_out, formatted_exc)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
+47
-5
@@ -13,6 +13,7 @@ from test.test_unittest.support import (
|
||||
LoggingResult,
|
||||
ResultWithNoStartTestRunStopTestRun,
|
||||
)
|
||||
from test.support.testcase import ExceptionIsLikeMixin
|
||||
|
||||
|
||||
def resultFactory(*_):
|
||||
@@ -604,7 +605,7 @@ class TestClassCleanup(unittest.TestCase):
|
||||
|
||||
|
||||
@support.force_not_colorized_test_class
|
||||
class TestModuleCleanUp(unittest.TestCase):
|
||||
class TestModuleCleanUp(ExceptionIsLikeMixin, unittest.TestCase):
|
||||
def test_add_and_do_ModuleCleanup(self):
|
||||
module_cleanups = []
|
||||
|
||||
@@ -646,11 +647,50 @@ class TestModuleCleanUp(unittest.TestCase):
|
||||
[(module_cleanup_good, (1, 2, 3),
|
||||
dict(four='hello', five='goodbye')),
|
||||
(module_cleanup_bad, (), {})])
|
||||
with self.assertRaises(CustomError) as e:
|
||||
with self.assertRaises(Exception) as e:
|
||||
unittest.case.doModuleCleanups()
|
||||
self.assertEqual(str(e.exception), 'CleanUpExc')
|
||||
self.assertExceptionIsLike(e.exception,
|
||||
ExceptionGroup('module cleanup failed',
|
||||
[CustomError('CleanUpExc')]))
|
||||
self.assertEqual(unittest.case._module_cleanups, [])
|
||||
|
||||
def test_doModuleCleanup_with_multiple_errors_in_addModuleCleanup(self):
|
||||
def module_cleanup_bad1():
|
||||
raise TypeError('CleanUpExc1')
|
||||
|
||||
def module_cleanup_bad2():
|
||||
raise ValueError('CleanUpExc2')
|
||||
|
||||
class Module:
|
||||
unittest.addModuleCleanup(module_cleanup_bad1)
|
||||
unittest.addModuleCleanup(module_cleanup_bad2)
|
||||
with self.assertRaises(ExceptionGroup) as e:
|
||||
unittest.case.doModuleCleanups()
|
||||
self.assertExceptionIsLike(e.exception,
|
||||
ExceptionGroup('module cleanup failed', [
|
||||
ValueError('CleanUpExc2'),
|
||||
TypeError('CleanUpExc1'),
|
||||
]))
|
||||
|
||||
def test_doModuleCleanup_with_exception_group_in_addModuleCleanup(self):
|
||||
def module_cleanup_bad():
|
||||
raise ExceptionGroup('CleanUpExc', [
|
||||
ValueError('CleanUpExc2'),
|
||||
TypeError('CleanUpExc1'),
|
||||
])
|
||||
|
||||
class Module:
|
||||
unittest.addModuleCleanup(module_cleanup_bad)
|
||||
with self.assertRaises(ExceptionGroup) as e:
|
||||
unittest.case.doModuleCleanups()
|
||||
self.assertExceptionIsLike(e.exception,
|
||||
ExceptionGroup('module cleanup failed', [
|
||||
ExceptionGroup('CleanUpExc', [
|
||||
ValueError('CleanUpExc2'),
|
||||
TypeError('CleanUpExc1'),
|
||||
]),
|
||||
]))
|
||||
|
||||
def test_addModuleCleanup_arg_errors(self):
|
||||
cleanups = []
|
||||
def cleanup(*args, **kwargs):
|
||||
@@ -871,9 +911,11 @@ class TestModuleCleanUp(unittest.TestCase):
|
||||
ordering = []
|
||||
blowUp = True
|
||||
suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
|
||||
with self.assertRaises(CustomError) as cm:
|
||||
with self.assertRaises(Exception) as cm:
|
||||
suite.debug()
|
||||
self.assertEqual(str(cm.exception), 'CleanUpExc')
|
||||
self.assertExceptionIsLike(cm.exception,
|
||||
ExceptionGroup('module cleanup failed',
|
||||
[CustomError('CleanUpExc')]))
|
||||
self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
|
||||
'tearDownClass', 'tearDownModule', 'cleanup_exc'])
|
||||
self.assertEqual(unittest.case._module_cleanups, [])
|
||||
|
||||
Vendored
+33
@@ -26,6 +26,39 @@ class TestUtil(unittest.TestCase):
|
||||
self.assertEqual(sorted_list_difference([2], [1, 1]), ([2], [1]))
|
||||
self.assertEqual(sorted_list_difference([1, 2], [1, 1]), ([2], []))
|
||||
|
||||
def test_sorted_list_difference_tail_deduplication(self):
|
||||
# Tail deduplication when one list is exhausted before the other.
|
||||
# These exercise the except-IndexError path in sorted_list_difference.
|
||||
self.assertEqual(sorted_list_difference([], [0, 0]), ([], [0]))
|
||||
self.assertEqual(sorted_list_difference([0, 0], []), ([0], []))
|
||||
self.assertEqual(sorted_list_difference([], [1, 1, 2, 2]), ([], [1, 2]))
|
||||
self.assertEqual(sorted_list_difference([1, 1, 2, 2], []), ([1, 2], []))
|
||||
# One list exhausts mid-way, leaving duplicated tail in the other.
|
||||
self.assertEqual(sorted_list_difference([1], [1, 2, 2, 3, 3]), ([], [2, 3]))
|
||||
self.assertEqual(sorted_list_difference([1, 2, 2, 3, 3], [1]), ([2, 3], []))
|
||||
|
||||
def test_sorted_list_difference_strings(self):
|
||||
self.assertEqual(
|
||||
sorted_list_difference(['a', 'b'], ['b', 'c']),
|
||||
(['a'], ['c']))
|
||||
self.assertEqual(
|
||||
sorted_list_difference([], ['a', 'a', 'b']),
|
||||
([], ['a', 'b']))
|
||||
self.assertEqual(
|
||||
sorted_list_difference(['a', 'a', 'b'], []),
|
||||
(['a', 'b'], []))
|
||||
|
||||
def test_sorted_list_difference_unhashable(self):
|
||||
self.assertEqual(
|
||||
sorted_list_difference([[1], [2]], [[2], [3]]),
|
||||
([[1]], [[3]]))
|
||||
self.assertEqual(
|
||||
sorted_list_difference([], [[0], [0]]),
|
||||
([], [[0]]))
|
||||
self.assertEqual(
|
||||
sorted_list_difference([[0], [0]], []),
|
||||
([[0]], []))
|
||||
|
||||
def test_unorderable_list_difference(self):
|
||||
self.assertEqual(unorderable_list_difference([], []), ([], []))
|
||||
self.assertEqual(unorderable_list_difference([1, 2], []), ([2, 1], []))
|
||||
|
||||
Vendored
+3
-2
@@ -30,7 +30,7 @@ class _AssertLogsContext(_BaseTestCaseContext):
|
||||
|
||||
LOGGING_FORMAT = "%(levelname)s:%(name)s:%(message)s"
|
||||
|
||||
def __init__(self, test_case, logger_name, level, no_logs):
|
||||
def __init__(self, test_case, logger_name, level, no_logs, formatter=None):
|
||||
_BaseTestCaseContext.__init__(self, test_case)
|
||||
self.logger_name = logger_name
|
||||
if level:
|
||||
@@ -39,13 +39,14 @@ class _AssertLogsContext(_BaseTestCaseContext):
|
||||
self.level = logging.INFO
|
||||
self.msg = None
|
||||
self.no_logs = no_logs
|
||||
self.formatter = formatter
|
||||
|
||||
def __enter__(self):
|
||||
if isinstance(self.logger_name, logging.Logger):
|
||||
logger = self.logger = self.logger_name
|
||||
else:
|
||||
logger = self.logger = logging.getLogger(self.logger_name)
|
||||
formatter = logging.Formatter(self.LOGGING_FORMAT)
|
||||
formatter = self.formatter or logging.Formatter(self.LOGGING_FORMAT)
|
||||
handler = _CapturingHandler()
|
||||
handler.setLevel(self.level)
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
Vendored
+31
-7
@@ -149,9 +149,7 @@ def doModuleCleanups():
|
||||
except Exception as exc:
|
||||
exceptions.append(exc)
|
||||
if exceptions:
|
||||
# Swallows all but first exception. If a multi-exception handler
|
||||
# gets written we should use that here instead.
|
||||
raise exceptions[0]
|
||||
raise ExceptionGroup('module cleanup failed', exceptions)
|
||||
|
||||
|
||||
def skip(reason):
|
||||
@@ -303,7 +301,7 @@ class _AssertWarnsContext(_AssertRaisesBaseContext):
|
||||
v.__warningregistry__ = {}
|
||||
self.warnings_manager = warnings.catch_warnings(record=True)
|
||||
self.warnings = self.warnings_manager.__enter__()
|
||||
warnings.simplefilter("always", self.expected)
|
||||
warnings.simplefilter("always")
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
@@ -316,19 +314,44 @@ class _AssertWarnsContext(_AssertRaisesBaseContext):
|
||||
except AttributeError:
|
||||
exc_name = str(self.expected)
|
||||
first_matching = None
|
||||
matched = False
|
||||
non_matching_warnings = []
|
||||
for m in self.warnings:
|
||||
w = m.message
|
||||
if not isinstance(w, self.expected):
|
||||
non_matching_warnings.append(m)
|
||||
continue
|
||||
if first_matching is None:
|
||||
first_matching = w
|
||||
if (self.expected_regex is not None and
|
||||
not self.expected_regex.search(str(w))):
|
||||
non_matching_warnings.append(m)
|
||||
continue
|
||||
if matched:
|
||||
continue
|
||||
matched = True
|
||||
# store warning for later retrieval
|
||||
self.warning = w
|
||||
self.filename = m.filename
|
||||
self.lineno = m.lineno
|
||||
for m in non_matching_warnings:
|
||||
module = m.module
|
||||
module_globals = None
|
||||
registry = None
|
||||
if module is not None:
|
||||
try:
|
||||
module_globals = vars(sys.modules[module])
|
||||
except (KeyError, TypeError):
|
||||
# module == "<string>" or sys.modules[module] is None
|
||||
pass
|
||||
else:
|
||||
registry = module_globals.setdefault("__warningregistry__", {})
|
||||
warnings.warn_explicit(m.message, m.category, m.filename, m.lineno,
|
||||
module=module,
|
||||
registry=registry,
|
||||
module_globals=module_globals,
|
||||
source=m.source)
|
||||
if matched:
|
||||
return
|
||||
# Now we simply try to choose a helpful failure message
|
||||
if first_matching is not None:
|
||||
@@ -340,7 +363,6 @@ class _AssertWarnsContext(_AssertRaisesBaseContext):
|
||||
else:
|
||||
self._raiseFailure("{} not triggered".format(exc_name))
|
||||
|
||||
|
||||
class _AssertNotWarnsContext(_AssertWarnsContext):
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
@@ -851,7 +873,7 @@ class TestCase(object):
|
||||
context = _AssertNotWarnsContext(expected_warning, self)
|
||||
return context.handle('_assertNotWarns', args, kwargs)
|
||||
|
||||
def assertLogs(self, logger=None, level=None):
|
||||
def assertLogs(self, logger=None, level=None, formatter=None):
|
||||
"""Fail unless a log message of level *level* or higher is emitted
|
||||
on *logger_name* or its children. If omitted, *level* defaults to
|
||||
INFO and *logger* defaults to the root logger.
|
||||
@@ -863,6 +885,8 @@ class TestCase(object):
|
||||
`records` attribute will be a list of the corresponding LogRecord
|
||||
objects.
|
||||
|
||||
Optionally supply `formatter` to control how messages are formatted.
|
||||
|
||||
Example::
|
||||
|
||||
with self.assertLogs('foo', level='INFO') as cm:
|
||||
@@ -873,7 +897,7 @@ class TestCase(object):
|
||||
"""
|
||||
# Lazy import to avoid importing logging if it is not needed.
|
||||
from ._log import _AssertLogsContext
|
||||
return _AssertLogsContext(self, logger, level, no_logs=False)
|
||||
return _AssertLogsContext(self, logger, level, no_logs=False, formatter=formatter)
|
||||
|
||||
def assertNoLogs(self, logger=None, level=None):
|
||||
""" Fail unless no log messages of level *level* or higher are emitted
|
||||
|
||||
Vendored
+9
-2
@@ -1,5 +1,6 @@
|
||||
"""Loading unittests."""
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
@@ -84,8 +85,10 @@ class TestLoader(object):
|
||||
raise TypeError("Test cases should not be derived from "
|
||||
"TestSuite. Maybe you meant to derive from "
|
||||
"TestCase?")
|
||||
if testCaseClass in (case.TestCase, case.FunctionTestCase):
|
||||
# We don't load any tests from base types that should not be loaded.
|
||||
if (testCaseClass in (case.TestCase, case.FunctionTestCase) or
|
||||
inspect.isabstract(testCaseClass)):
|
||||
# We don't load any tests from base types that should not be loaded,
|
||||
# and abstract base classes that can't be instantiated
|
||||
testCaseNames = []
|
||||
else:
|
||||
testCaseNames = self.getTestCaseNames(testCaseClass)
|
||||
@@ -103,6 +106,7 @@ class TestLoader(object):
|
||||
isinstance(obj, type)
|
||||
and issubclass(obj, case.TestCase)
|
||||
and obj not in (case.TestCase, case.FunctionTestCase)
|
||||
and not inspect.isabstract(obj)
|
||||
):
|
||||
tests.append(self.loadTestsFromTestCase(obj))
|
||||
|
||||
@@ -181,6 +185,9 @@ class TestLoader(object):
|
||||
elif (isinstance(obj, types.FunctionType) and
|
||||
isinstance(parent, type) and
|
||||
issubclass(parent, case.TestCase)):
|
||||
if inspect.isabstract(parent):
|
||||
raise TypeError(
|
||||
"Cannot instantiate abstract test case %s" % parent.__name__)
|
||||
name = parts[-1]
|
||||
inst = parent(name)
|
||||
# static methods follow a different path
|
||||
|
||||
Vendored
+7
-7
@@ -172,7 +172,7 @@ class TestProgram(object):
|
||||
help='Show local variables in tracebacks')
|
||||
parser.add_argument('--durations', dest='durations', type=int,
|
||||
default=None, metavar="N",
|
||||
help='Show the N slowest test cases (N=0 for all)')
|
||||
help='Show the `N` slowest test cases (`N=0` for all)')
|
||||
if self.failfast is None:
|
||||
parser.add_argument('-f', '--failfast', dest='failfast',
|
||||
action='store_true',
|
||||
@@ -181,12 +181,12 @@ class TestProgram(object):
|
||||
if self.catchbreak is None:
|
||||
parser.add_argument('-c', '--catch', dest='catchbreak',
|
||||
action='store_true',
|
||||
help='Catch Ctrl-C and display results so far')
|
||||
help='Catch `Ctrl-C` and display results so far')
|
||||
self.catchbreak = False
|
||||
if self.buffer is None:
|
||||
parser.add_argument('-b', '--buffer', dest='buffer',
|
||||
action='store_true',
|
||||
help='Buffer stdout and stderr during tests')
|
||||
help='Buffer `stdout` and `stderr` during tests')
|
||||
self.buffer = False
|
||||
if self.testNamePatterns is None:
|
||||
parser.add_argument('-k', dest='testNamePatterns',
|
||||
@@ -197,7 +197,7 @@ class TestProgram(object):
|
||||
return parser
|
||||
|
||||
def _getMainArgParser(self, parent):
|
||||
parser = argparse.ArgumentParser(parents=[parent], color=True)
|
||||
parser = argparse.ArgumentParser(parents=[parent])
|
||||
parser.prog = self.progName
|
||||
parser.print_help = self._print_help
|
||||
|
||||
@@ -208,16 +208,16 @@ class TestProgram(object):
|
||||
return parser
|
||||
|
||||
def _getDiscoveryArgParser(self, parent):
|
||||
parser = argparse.ArgumentParser(parents=[parent], color=True)
|
||||
parser = argparse.ArgumentParser(parents=[parent])
|
||||
parser.prog = '%s discover' % self.progName
|
||||
parser.epilog = ('For test discovery all test modules must be '
|
||||
'importable from the top level directory of the '
|
||||
'project.')
|
||||
|
||||
parser.add_argument('-s', '--start-directory', dest='start',
|
||||
help="Directory to start discovery ('.' default)")
|
||||
help="Directory to start discovery (`.` default)")
|
||||
parser.add_argument('-p', '--pattern', dest='pattern',
|
||||
help="Pattern to match tests ('test*.py' default)")
|
||||
help="Pattern to match tests (`test*.py` default)")
|
||||
parser.add_argument('-t', '--top-level-directory', dest='top',
|
||||
help='Top level directory of project (defaults to '
|
||||
'start directory)')
|
||||
|
||||
Vendored
+1
-2
@@ -4,11 +4,10 @@ import sys
|
||||
import time
|
||||
import warnings
|
||||
|
||||
from _colorize import get_theme
|
||||
|
||||
from . import result
|
||||
from .case import _SubTest
|
||||
from .signals import registerResult
|
||||
lazy from _colorize import get_theme
|
||||
|
||||
__unittest = True
|
||||
|
||||
|
||||
Vendored
+16
-4
@@ -223,6 +223,11 @@ class TestSuite(BaseTestSuite):
|
||||
if result._moduleSetUpFailed:
|
||||
try:
|
||||
case.doModuleCleanups()
|
||||
except ExceptionGroup as eg:
|
||||
for e in eg.exceptions:
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'setUpModule',
|
||||
currentModule)
|
||||
except Exception as e:
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'setUpModule',
|
||||
@@ -235,15 +240,15 @@ class TestSuite(BaseTestSuite):
|
||||
errorName = f'{method_name} ({parent})'
|
||||
self._addClassOrModuleLevelException(result, exc, errorName, info)
|
||||
|
||||
def _addClassOrModuleLevelException(self, result, exception, errorName,
|
||||
def _addClassOrModuleLevelException(self, result, exc, errorName,
|
||||
info=None):
|
||||
error = _ErrorHolder(errorName)
|
||||
addSkip = getattr(result, 'addSkip', None)
|
||||
if addSkip is not None and isinstance(exception, case.SkipTest):
|
||||
addSkip(error, str(exception))
|
||||
if addSkip is not None and isinstance(exc, case.SkipTest):
|
||||
addSkip(error, str(exc))
|
||||
else:
|
||||
if not info:
|
||||
result.addError(error, sys.exc_info())
|
||||
result.addError(error, (type(exc), exc, exc.__traceback__))
|
||||
else:
|
||||
result.addError(error, info)
|
||||
|
||||
@@ -273,6 +278,13 @@ class TestSuite(BaseTestSuite):
|
||||
previousModule)
|
||||
try:
|
||||
case.doModuleCleanups()
|
||||
except ExceptionGroup as eg:
|
||||
if isinstance(result, _DebugResult):
|
||||
raise
|
||||
for e in eg.exceptions:
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'tearDownModule',
|
||||
previousModule)
|
||||
except Exception as e:
|
||||
if isinstance(result, _DebugResult):
|
||||
raise
|
||||
|
||||
Vendored
+21
-4
@@ -1,7 +1,6 @@
|
||||
"""Various utility functions."""
|
||||
|
||||
from collections import namedtuple, Counter
|
||||
from os.path import commonprefix
|
||||
|
||||
__unittest = True
|
||||
|
||||
@@ -21,13 +20,23 @@ def _shorten(s, prefixlen, suffixlen):
|
||||
s = '%s[%d chars]%s' % (s[:prefixlen], skip, s[len(s) - suffixlen:])
|
||||
return s
|
||||
|
||||
def _common_prefix(m):
|
||||
if not m:
|
||||
return ""
|
||||
s1 = min(m)
|
||||
s2 = max(m)
|
||||
for i, c in enumerate(s1):
|
||||
if c != s2[i]:
|
||||
return s1[:i]
|
||||
return s1
|
||||
|
||||
def _common_shorten_repr(*args):
|
||||
args = tuple(map(safe_repr, args))
|
||||
maxlen = max(map(len, args))
|
||||
if maxlen <= _MAX_LENGTH:
|
||||
return args
|
||||
|
||||
prefix = commonprefix(args)
|
||||
prefix = _common_prefix(args)
|
||||
prefixlen = len(prefix)
|
||||
|
||||
common_len = _MAX_LENGTH - \
|
||||
@@ -54,6 +63,14 @@ def safe_repr(obj, short=False):
|
||||
def strclass(cls):
|
||||
return "%s.%s" % (cls.__module__, cls.__qualname__)
|
||||
|
||||
def _dedupe_sorted(lst):
|
||||
"""Remove consecutive duplicate elements from a sorted list."""
|
||||
result = []
|
||||
for item in lst:
|
||||
if not result or result[-1] != item:
|
||||
result.append(item)
|
||||
return result
|
||||
|
||||
def sorted_list_difference(expected, actual):
|
||||
"""Finds elements in only one or the other of two, sorted input lists.
|
||||
|
||||
@@ -89,8 +106,8 @@ def sorted_list_difference(expected, actual):
|
||||
while actual[j] == a:
|
||||
j += 1
|
||||
except IndexError:
|
||||
missing.extend(expected[i:])
|
||||
unexpected.extend(actual[j:])
|
||||
missing.extend(_dedupe_sorted(expected[i:]))
|
||||
unexpected.extend(_dedupe_sorted(actual[j:]))
|
||||
break
|
||||
return missing, unexpected
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ mod _collections {
|
||||
VirtualMachine, atomic_func,
|
||||
builtins::{
|
||||
IterStatus::{Active, Exhausted},
|
||||
PositionIterInternal, PyDict, PyGenericAlias, PyInt, PyList, PyStr, PyTuple, PyType,
|
||||
PyTypeRef, locked_step,
|
||||
PositionIterInternal, PyAnyDict, PyDict, PyGenericAlias, PyInt, PyList, PyStr, PyTuple,
|
||||
PyType, PyTypeRef, locked_step,
|
||||
},
|
||||
common::lock::{PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard},
|
||||
convert::ToPyObject,
|
||||
@@ -1072,7 +1072,7 @@ mod _collections {
|
||||
rhs.class().name()
|
||||
)));
|
||||
};
|
||||
if !other.fast_isinstance(vm.ctx.types.dict_type) {
|
||||
if PyAnyDict::from_object(other).is_none() {
|
||||
return not_implemented();
|
||||
}
|
||||
|
||||
|
||||
@@ -684,9 +684,7 @@ pub(crate) mod ordered_dict {
|
||||
fn as_number() -> &'static PyNumberMethods {
|
||||
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
|
||||
or: Some(|a, b, vm| {
|
||||
if !a.fast_isinstance(vm.ctx.types.dict_type)
|
||||
|| !b.fast_isinstance(vm.ctx.types.dict_type)
|
||||
{
|
||||
if PyAnyDict::from_object(a).is_none() || PyAnyDict::from_object(b).is_none() {
|
||||
return Ok(vm.ctx.not_implemented());
|
||||
}
|
||||
let cls = if let Some(od) = a.downcast_ref::<PyOrderedDict>() {
|
||||
|
||||
Reference in new issue
Block a user