Merge pull request #3917 from youknowone/update-unittest

Update more tests
This commit is contained in:
Jeong YunWon
2022-07-19 03:21:44 +09:00
committed by GitHub
9 changed files with 282 additions and 116 deletions

186
Lib/datetime.py vendored
View File

@@ -4,9 +4,14 @@ See http://www.iana.org/time-zones/repository/tz-link.html for
time zone and DST data sources.
"""
__all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo",
"MINYEAR", "MAXYEAR")
import time as _time
import math as _math
import sys
from operator import index as _index
def _cmp(x, y):
return 0 if x == y else 1 if x > y else -1
@@ -376,27 +381,10 @@ def _check_utc_offset(name, offset):
"-timedelta(hours=24) and timedelta(hours=24)" %
(name, offset))
def _check_int_field(value):
if isinstance(value, int):
return value
if not isinstance(value, float):
try:
value = value.__int__()
except AttributeError:
pass
else:
if isinstance(value, int):
return value
raise TypeError('__int__ returned non-int (type %s)' %
type(value).__name__)
raise TypeError('an integer is required (got type %s)' %
type(value).__name__)
raise TypeError('integer argument expected, got float')
def _check_date_fields(year, month, day):
year = _check_int_field(year)
month = _check_int_field(month)
day = _check_int_field(day)
year = _index(year)
month = _index(month)
day = _index(day)
if not MINYEAR <= year <= MAXYEAR:
raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)
if not 1 <= month <= 12:
@@ -407,10 +395,10 @@ def _check_date_fields(year, month, day):
return year, month, day
def _check_time_fields(hour, minute, second, microsecond, fold):
hour = _check_int_field(hour)
minute = _check_int_field(minute)
second = _check_int_field(second)
microsecond = _check_int_field(microsecond)
hour = _index(hour)
minute = _index(minute)
second = _index(second)
microsecond = _index(microsecond)
if not 0 <= hour <= 23:
raise ValueError('hour must be in 0..23', hour)
if not 0 <= minute <= 59:
@@ -718,31 +706,31 @@ class timedelta:
if isinstance(other, timedelta):
return self._cmp(other) == 0
else:
return False
return NotImplemented
def __le__(self, other):
if isinstance(other, timedelta):
return self._cmp(other) <= 0
else:
_cmperror(self, other)
return NotImplemented
def __lt__(self, other):
if isinstance(other, timedelta):
return self._cmp(other) < 0
else:
_cmperror(self, other)
return NotImplemented
def __ge__(self, other):
if isinstance(other, timedelta):
return self._cmp(other) >= 0
else:
_cmperror(self, other)
return NotImplemented
def __gt__(self, other):
if isinstance(other, timedelta):
return self._cmp(other) > 0
else:
_cmperror(self, other)
return NotImplemented
def _cmp(self, other):
assert isinstance(other, timedelta)
@@ -869,6 +857,40 @@ class date:
except Exception:
raise ValueError(f'Invalid isoformat string: {date_string!r}')
@classmethod
def fromisocalendar(cls, year, week, day):
"""Construct a date from the ISO year, week number and weekday.
This is the inverse of the date.isocalendar() function"""
# Year is bounded this way because 9999-12-31 is (9999, 52, 5)
if not MINYEAR <= year <= MAXYEAR:
raise ValueError(f"Year is out of range: {year}")
if not 0 < week < 53:
out_of_range = True
if week == 53:
# ISO years have 53 weeks in them on years starting with a
# Thursday and leap years starting on a Wednesday
first_weekday = _ymd2ord(year, 1, 1) % 7
if (first_weekday == 4 or (first_weekday == 3 and
_is_leap(year))):
out_of_range = False
if out_of_range:
raise ValueError(f"Invalid week: {week}")
if not 0 < day < 8:
raise ValueError(f"Invalid weekday: {day} (range is [1, 7])")
# Now compute the offset from (Y, 1, 1) in days:
day_offset = (week - 1) * 7 + (day - 1)
# Calculate the ordinal day for monday, week 1
day_1 = _isoweek1monday(year)
ord_day = day_1 + day_offset
return cls(*_ord2ymd(ord_day))
# Conversions to string
@@ -1014,7 +1036,7 @@ class date:
if isinstance(other, timedelta):
o = self.toordinal() + other.days
if 0 < o <= _MAXORDINAL:
return date.fromordinal(o)
return type(self).fromordinal(o)
raise OverflowError("result out of range")
return NotImplemented
@@ -1042,7 +1064,7 @@ class date:
return self.toordinal() % 7 or 7
def isocalendar(self):
"""Return a 3-tuple containing ISO year, week number, and weekday.
"""Return a named tuple containing ISO year, week number, and weekday.
The first ISO week of the year is the (Mon-Sun) week
containing the year's first Thursday; everything else derives
@@ -1067,7 +1089,7 @@ class date:
if today >= _isoweek1monday(year+1):
year += 1
week = 0
return year, week+1, day+1
return _IsoCalendarDate(year, week+1, day+1)
# Pickle support.
@@ -1157,6 +1179,36 @@ class tzinfo:
else:
return (self.__class__, args, state)
class IsoCalendarDate(tuple):
def __new__(cls, year, week, weekday, /):
return super().__new__(cls, (year, week, weekday))
@property
def year(self):
return self[0]
@property
def week(self):
return self[1]
@property
def weekday(self):
return self[2]
def __reduce__(self):
# This code is intended to pickle the object without making the
# class public. See https://bugs.python.org/msg352381
return (tuple, (tuple(self),))
def __repr__(self):
return (f'{self.__class__.__name__}'
f'(year={self[0]}, week={self[1]}, weekday={self[2]})')
_IsoCalendarDate = IsoCalendarDate
del IsoCalendarDate
_tzinfo_class = tzinfo
class time:
@@ -1261,31 +1313,31 @@ class time:
if isinstance(other, time):
return self._cmp(other, allow_mixed=True) == 0
else:
return False
return NotImplemented
def __le__(self, other):
if isinstance(other, time):
return self._cmp(other) <= 0
else:
_cmperror(self, other)
return NotImplemented
def __lt__(self, other):
if isinstance(other, time):
return self._cmp(other) < 0
else:
_cmperror(self, other)
return NotImplemented
def __ge__(self, other):
if isinstance(other, time):
return self._cmp(other) >= 0
else:
_cmperror(self, other)
return NotImplemented
def __gt__(self, other):
if isinstance(other, time):
return self._cmp(other) > 0
else:
_cmperror(self, other)
return NotImplemented
def _cmp(self, other, allow_mixed=False):
assert isinstance(other, time)
@@ -1369,7 +1421,8 @@ class time:
part is omitted if self.microsecond == 0.
The optional argument timespec specifies the number of additional
terms of the time to include.
terms of the time to include. Valid options are 'auto', 'hours',
'minutes', 'seconds', 'milliseconds' and 'microseconds'.
"""
s = _format_time(self._hour, self._minute, self._second,
self._microsecond, timespec)
@@ -1495,7 +1548,7 @@ class time:
self._tzinfo = tzinfo
def __reduce_ex__(self, protocol):
return (time, self._getstate(protocol))
return (self.__class__, self._getstate(protocol))
def __reduce__(self):
return self.__reduce_ex__(2)
@@ -1506,6 +1559,7 @@ time.min = time(0, 0, 0)
time.max = time(23, 59, 59, 999999)
time.resolution = timedelta(microseconds=1)
class datetime(date):
"""datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
@@ -1598,7 +1652,7 @@ class datetime(date):
y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
ss = min(ss, 59) # clamp out leap seconds if the platform has them
result = cls(y, m, d, hh, mm, ss, us, tz)
if tz is None:
if tz is None and not utc:
# As of version 2015f max fold in IANA database is
# 23 hours at 1969-09-30 13:00:00 in Kwajalein.
# Let's probe 24 hours in the past to detect a transition:
@@ -1619,7 +1673,7 @@ class datetime(date):
probe2 = cls(y, m, d, hh, mm, ss, us, tz)
if probe2 == result:
result._fold = 1
else:
elif tz is not None:
result = tz.fromutc(result)
return result
@@ -1798,17 +1852,10 @@ class datetime(date):
ts = (self - _EPOCH) // timedelta(seconds=1)
localtm = _time.localtime(ts)
local = datetime(*localtm[:6])
try:
# Extract TZ data if available
gmtoff = localtm.tm_gmtoff
zone = localtm.tm_zone
except AttributeError:
delta = local - datetime(*_time.gmtime(ts)[:6])
zone = _time.strftime('%Z', localtm)
tz = timezone(delta, zone)
else:
tz = timezone(timedelta(seconds=gmtoff), zone)
return tz
# Extract TZ data
gmtoff = localtm.tm_gmtoff
zone = localtm.tm_zone
return timezone(timedelta(seconds=gmtoff), zone)
def astimezone(self, tz=None):
if tz is None:
@@ -1860,7 +1907,8 @@ class datetime(date):
time, default 'T'.
The optional argument timespec specifies the number of additional
terms of the time to include.
terms of the time to include. Valid options are 'auto', 'hours',
'minutes', 'seconds', 'milliseconds' and 'microseconds'.
"""
s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) +
_format_time(self._hour, self._minute, self._second,
@@ -2031,10 +2079,10 @@ class datetime(date):
hour, rem = divmod(delta.seconds, 3600)
minute, second = divmod(rem, 60)
if 0 < delta.days <= _MAXORDINAL:
return datetime.combine(date.fromordinal(delta.days),
time(hour, minute, second,
delta.microseconds,
tzinfo=self._tzinfo))
return type(self).combine(date.fromordinal(delta.days),
time(hour, minute, second,
delta.microseconds,
tzinfo=self._tzinfo))
raise OverflowError("result out of range")
__radd__ = __add__
@@ -2133,6 +2181,7 @@ def _isoweek1monday(year):
week1monday += 7
return week1monday
class timezone(tzinfo):
__slots__ = '_offset', '_name'
@@ -2167,9 +2216,9 @@ class timezone(tzinfo):
return (self._offset, self._name)
def __eq__(self, other):
if type(other) != timezone:
return False
return self._offset == other._offset
if isinstance(other, timezone):
return self._offset == other._offset
return NotImplemented
def __hash__(self):
return hash(self._offset)
@@ -2226,7 +2275,7 @@ class timezone(tzinfo):
raise TypeError("fromutc() argument must be a datetime instance"
" or None")
_maxoffset = timedelta(hours=23, minutes=59)
_maxoffset = timedelta(hours=24, microseconds=-1)
_minoffset = -_maxoffset
@staticmethod
@@ -2250,8 +2299,11 @@ class timezone(tzinfo):
return f'UTC{sign}{hours:02d}:{minutes:02d}'
timezone.utc = timezone._create(timedelta(0))
timezone.min = timezone._create(timezone._minoffset)
timezone.max = timezone._create(timezone._maxoffset)
# bpo-37642: These attributes are rounded to the nearest minute for backwards
# compatibility, even though the constructor will accept a wider range of
# values. This may change in the future.
timezone.min = timezone._create(-timedelta(hours=23, minutes=59))
timezone.max = timezone._create(timedelta(hours=23, minutes=59))
_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
# Some time zone algebra. For a datetime x, let
@@ -2275,7 +2327,7 @@ _EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
# This is again a requirement for a sane tzinfo class.
#
# 4. (x+k).s = x.s
# This follows from #2, and that datimetimetz+timedelta preserves tzinfo.
# This follows from #2, and that datetime.timetz+timedelta preserves tzinfo.
#
# 5. (x+k).n = x.n + k
# Again follows from how arithmetic is defined.
@@ -2458,13 +2510,13 @@ else:
# Clean up unused names
del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH, _DI100Y, _DI400Y,
_DI4Y, _EPOCH, _MAXORDINAL, _MONTHNAMES, _build_struct_time,
_check_date_fields, _check_int_field, _check_time_fields,
_check_date_fields, _check_time_fields,
_check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror,
_date_class, _days_before_month, _days_before_year, _days_in_month,
_format_time, _format_offset, _is_leap, _isoweek1monday, _math,
_format_time, _format_offset, _index, _is_leap, _isoweek1monday, _math,
_ord2ymd, _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord,
_divide_and_round, _parse_isoformat_date, _parse_isoformat_time,
_parse_hh_mm_ss_ff)
_parse_hh_mm_ss_ff, _IsoCalendarDate)
# XXX Since import * above excludes names that start with _,
# docstring does not get overwritten. In the future, it may be
# appropriate to maintain a single module level docstring and

15
Lib/json/__init__.py vendored
View File

@@ -133,7 +133,7 @@ def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True,
If ``check_circular`` is false, then the circular reference check
for container types will be skipped and a circular reference will
result in an ``OverflowError`` (or worse).
result in an ``RecursionError`` (or worse).
If ``allow_nan`` is false, then it will be a ``ValueError`` to
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
@@ -195,7 +195,7 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
If ``check_circular`` is false, then the circular reference check
for container types will be skipped and a circular reference will
result in an ``OverflowError`` (or worse).
result in an ``RecursionError`` (or worse).
If ``allow_nan`` is false, then it will be a ``ValueError`` to
serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
@@ -329,8 +329,6 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None,
To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
kwarg; otherwise ``JSONDecoder`` is used.
The ``encoding`` argument is ignored and deprecated since Python 3.1.
"""
if isinstance(s, str):
if s.startswith('\ufeff'):
@@ -342,15 +340,6 @@ def loads(s, *, cls=None, object_hook=None, parse_float=None,
f'not {s.__class__.__name__}')
s = s.decode(detect_encoding(s), 'surrogatepass')
if "encoding" in kw:
import warnings
warnings.warn(
"'encoding' is ignored and deprecated. It will be removed in Python 3.9",
DeprecationWarning,
stacklevel=2
)
del kw['encoding']
if (cls is None and object_hook is None and
parse_int is None and parse_float is None and
parse_constant is None and object_pairs_hook is None and not kw):

2
Lib/json/encoder.py vendored
View File

@@ -116,7 +116,7 @@ class JSONEncoder(object):
If check_circular is true, then lists, dicts, and custom encoded
objects will be checked for circular references during encoding to
prevent an infinite recursion (which would cause an OverflowError).
prevent an infinite recursion (which would cause an RecursionError).
Otherwise, no such check takes place.
If allow_nan is true, then NaN, Infinity, and -Infinity will be

58
Lib/json/tool.py vendored
View File

@@ -13,6 +13,7 @@ Usage::
import argparse
import json
import sys
from pathlib import Path
def main():
@@ -25,31 +26,60 @@ def main():
help='a JSON file to be validated or pretty-printed',
default=sys.stdin)
parser.add_argument('outfile', nargs='?',
type=argparse.FileType('w', encoding="utf-8"),
type=Path,
help='write the output of infile to outfile',
default=sys.stdout)
default=None)
parser.add_argument('--sort-keys', action='store_true', default=False,
help='sort the output of dictionaries alphabetically by key')
parser.add_argument('--no-ensure-ascii', dest='ensure_ascii', action='store_false',
help='disable escaping of non-ASCII characters')
parser.add_argument('--json-lines', action='store_true', default=False,
help='parse input using the jsonlines format')
help='parse input using the JSON Lines format. '
'Use with --no-indent or --compact to produce valid JSON Lines output.')
group = parser.add_mutually_exclusive_group()
group.add_argument('--indent', default=4, type=int,
help='separate items with newlines and use this number '
'of spaces for indentation')
group.add_argument('--tab', action='store_const', dest='indent',
const='\t', help='separate items with newlines and use '
'tabs for indentation')
group.add_argument('--no-indent', action='store_const', dest='indent',
const=None,
help='separate items with spaces rather than newlines')
group.add_argument('--compact', action='store_true',
help='suppress all whitespace separation (most compact)')
options = parser.parse_args()
infile = options.infile
outfile = options.outfile
sort_keys = options.sort_keys
json_lines = options.json_lines
with infile, outfile:
dump_args = {
'sort_keys': options.sort_keys,
'indent': options.indent,
'ensure_ascii': options.ensure_ascii,
}
if options.compact:
dump_args['indent'] = None
dump_args['separators'] = ',', ':'
with options.infile as infile:
try:
if json_lines:
if options.json_lines:
objs = (json.loads(line) for line in infile)
else:
objs = (json.load(infile), )
for obj in objs:
json.dump(obj, outfile, sort_keys=sort_keys, indent=4)
outfile.write('\n')
objs = (json.load(infile),)
if options.outfile is None:
out = sys.stdout
else:
out = options.outfile.open('w', encoding='utf-8')
with out as outfile:
for obj in objs:
json.dump(obj, outfile, **dump_args)
outfile.write('\n')
except ValueError as e:
raise SystemExit(e)
if __name__ == '__main__':
main()
try:
main()
except BrokenPipeError as exc:
sys.exit(exc.errno)

View File

@@ -6,6 +6,7 @@ import unittest
from test import support
from test.support import import_helper
# import json with and without accelerations
# XXX RUSTPYTHON: we don't import _json as fresh since the fresh module isn't placed
# into the sys.modules cache, and therefore the vm can't recognize the _json.Scanner class
@@ -40,6 +41,7 @@ class TestPyTest(PyTest):
'json.encoder')
class TestCTest(CTest):
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_cjson(self):
self.assertEqual(self.json.scanner.make_scanner.__module__, '_json')

View File

@@ -97,10 +97,6 @@ class TestDecode:
d = self.json.JSONDecoder()
self.assertRaises(ValueError, d.raw_decode, 'a'*42, -50000)
def test_deprecated_encode(self):
with self.assertWarns(DeprecationWarning):
self.loads('{}', encoding='fake')
class TestPyDecode(TestDecode, PyTest): pass
# TODO: RUSTPYTHON
class TestCDecode(TestDecode, CTest): # pass

View File

@@ -1,3 +1,4 @@
from test import support
from test.test_json import PyTest, CTest
@@ -52,7 +53,7 @@ class TestRecursion:
return [JSONTestObject]
else:
return 'JSONTestObject'
return pyjson.JSONEncoder.default(o)
return self.json.JSONEncoder.default(o)
enc = RecursiveJSONEncoder()
self.assertEqual(enc.encode(JSONTestObject), '"JSONTestObject"')
@@ -69,11 +70,14 @@ class TestRecursion:
# test that loading highly-nested objects doesn't segfault when C
# accelerations are used. See #12017
with self.assertRaises(RecursionError):
self.loads('{"a":' * 100000 + '1' + '}' * 100000)
with support.infinite_recursion():
self.loads('{"a":' * 100000 + '1' + '}' * 100000)
with self.assertRaises(RecursionError):
self.loads('{"a":' * 100000 + '[1]' + '}' * 100000)
with support.infinite_recursion():
self.loads('{"a":' * 100000 + '[1]' + '}' * 100000)
with self.assertRaises(RecursionError):
self.loads('[' * 100000 + '1' + ']' * 100000)
with support.infinite_recursion():
self.loads('[' * 100000 + '1' + ']' * 100000)
def test_highly_nested_objects_encoding(self):
# See #12051
@@ -81,9 +85,11 @@ class TestRecursion:
for x in range(100000):
l, d = [l], {'k':d}
with self.assertRaises(RecursionError):
self.dumps(l)
with support.infinite_recursion():
self.dumps(l)
with self.assertRaises(RecursionError):
self.dumps(d)
with support.infinite_recursion():
self.dumps(d)
def test_endless_recursion(self):
# See #12051
@@ -93,7 +99,8 @@ class TestRecursion:
return [o]
with self.assertRaises(RecursionError):
EndlessJSONEncoder(check_circular=False).encode(5j)
with support.infinite_recursion():
EndlessJSONEncoder(check_circular=False).encode(5j)
class TestPyRecursion(TestRecursion, PyTest): pass

View File

@@ -62,6 +62,17 @@ class TestEncode(CTest):
with self.assertRaises(ZeroDivisionError):
enc('spam', 4)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bad_markers_argument_to_encoder(self):
# https://bugs.python.org/issue45269
with self.assertRaisesRegex(
TypeError,
r'make_encoder\(\) argument 1 must be dict or None, not int',
):
self.json.encoder.c_make_encoder(1, None, None, None, ': ', ', ',
False, False, False)
# TODO: RUSTPYTHON, translate the encoder to Rust
@unittest.expectedFailure
def test_bad_bool_args(self):

View File

@@ -1,8 +1,10 @@
import errno
import os
import sys
import textwrap
import unittest
from subprocess import Popen, PIPE
import subprocess
from test import support
from test.support import os_helper
from test.support.script_helper import assert_python_ok
@@ -85,10 +87,9 @@ class TestTool(unittest.TestCase):
def test_stdin_stdout(self):
args = sys.executable, '-m', 'json.tool'
with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc:
out, err = proc.communicate(self.data.encode())
self.assertEqual(out.splitlines(), self.expect.encode().splitlines())
self.assertEqual(err, b'')
process = subprocess.run(args, input=self.data, capture_output=True, text=True, check=True)
self.assertEqual(process.stdout, self.expect)
self.assertEqual(process.stderr, '')
def _create_infile(self, data=None):
infile = os_helper.TESTFN
@@ -124,7 +125,16 @@ class TestTool(unittest.TestCase):
outfile = os_helper.TESTFN + '.out'
rc, out, err = assert_python_ok('-m', 'json.tool', infile, outfile)
self.addCleanup(os.remove, outfile)
with open(outfile, "r") as fp:
with open(outfile, "r", encoding="utf-8") as fp:
self.assertEqual(fp.read(), self.expect)
self.assertEqual(rc, 0)
self.assertEqual(out, b'')
self.assertEqual(err, b'')
def test_writing_in_place(self):
infile = self._create_infile()
rc, out, err = assert_python_ok('-m', 'json.tool', infile, infile)
with open(infile, "r", encoding="utf-8") as fp:
self.assertEqual(fp.read(), self.expect)
self.assertEqual(rc, 0)
self.assertEqual(out, b'')
@@ -132,10 +142,9 @@ class TestTool(unittest.TestCase):
def test_jsonlines(self):
args = sys.executable, '-m', 'json.tool', '--json-lines'
with Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc:
out, err = proc.communicate(self.jsonlines_raw.encode())
self.assertEqual(out.splitlines(), self.jsonlines_expect.encode().splitlines())
self.assertEqual(err, b'')
process = subprocess.run(args, input=self.jsonlines_raw, capture_output=True, text=True, check=True)
self.assertEqual(process.stdout, self.jsonlines_expect)
self.assertEqual(process.stderr, '')
def test_help_flag(self):
rc, out, err = assert_python_ok('-m', 'json.tool', '-h')
@@ -150,3 +159,73 @@ class TestTool(unittest.TestCase):
self.assertEqual(out.splitlines(),
self.expect_without_sort_keys.encode().splitlines())
self.assertEqual(err, b'')
def test_indent(self):
input_ = '[1, 2]'
expect = textwrap.dedent('''\
[
1,
2
]
''')
args = sys.executable, '-m', 'json.tool', '--indent', '2'
process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True)
self.assertEqual(process.stdout, expect)
self.assertEqual(process.stderr, '')
def test_no_indent(self):
input_ = '[1,\n2]'
expect = '[1, 2]\n'
args = sys.executable, '-m', 'json.tool', '--no-indent'
process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True)
self.assertEqual(process.stdout, expect)
self.assertEqual(process.stderr, '')
def test_tab(self):
input_ = '[1, 2]'
expect = '[\n\t1,\n\t2\n]\n'
args = sys.executable, '-m', 'json.tool', '--tab'
process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True)
self.assertEqual(process.stdout, expect)
self.assertEqual(process.stderr, '')
def test_compact(self):
input_ = '[ 1 ,\n 2]'
expect = '[1,2]\n'
args = sys.executable, '-m', 'json.tool', '--compact'
process = subprocess.run(args, input=input_, capture_output=True, text=True, check=True)
self.assertEqual(process.stdout, expect)
self.assertEqual(process.stderr, '')
def test_no_ensure_ascii_flag(self):
infile = self._create_infile('{"key":"💩"}')
outfile = os_helper.TESTFN + '.out'
self.addCleanup(os.remove, outfile)
assert_python_ok('-m', 'json.tool', '--no-ensure-ascii', infile, outfile)
with open(outfile, "rb") as f:
lines = f.read().splitlines()
# asserting utf-8 encoded output file
expected = [b'{', b' "key": "\xf0\x9f\x92\xa9"', b"}"]
self.assertEqual(lines, expected)
def test_ensure_ascii_default(self):
infile = self._create_infile('{"key":"💩"}')
outfile = os_helper.TESTFN + '.out'
self.addCleanup(os.remove, outfile)
assert_python_ok('-m', 'json.tool', infile, outfile)
with open(outfile, "rb") as f:
lines = f.read().splitlines()
# asserting an ascii encoded output file
expected = [b'{', rb' "key": "\ud83d\udca9"', b"}"]
self.assertEqual(lines, expected)
@unittest.skipIf(sys.platform =="win32", "The test is failed with ValueError on Windows")
def test_broken_pipe_error(self):
cmd = [sys.executable, '-m', 'json.tool']
proc = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
# bpo-39828: Closing before json.tool attempts to write into stdout.
proc.stdout.close()
proc.communicate(b'"{}"')
self.assertEqual(proc.returncode, errno.EPIPE)