Skip to content

Commit 8736b20

Browse files
committed
BUG: Fix pd.show_versions as_json invalid JSON (pandas-dev#39701)
1 parent bcf2406 commit 8736b20

File tree

3 files changed

+61
-12
lines changed

3 files changed

+61
-12
lines changed

doc/source/whatsnew/v1.3.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ Other
461461
- Bug in :class:`Styler` where ``subset`` arg in methods raised an error for some valid multiindex slices (:issue:`33562`)
462462
- :class:`Styler` rendered HTML output minor alterations to support w3 good code standard (:issue:`39626`)
463463
- Bug in :meth:`DataFrame.equals`, :meth:`Series.equals`, :meth:`Index.equals` with object-dtype containing ``np.datetime64("NaT")`` or ``np.timedelta64("NaT")`` (:issue:`39650`)
464+
- Bug in :func:`pandas.util.show_versions` where console JSON output was not proper JSON (:issue:`39701`)
464465

465466

466467
.. ---------------------------------------------------------------------------

pandas/tests/util/test_show_versions.py

+59-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
import json
2+
import os
13
import re
24

35
import pytest
46

7+
from pandas.util._print_versions import _get_dependency_info, _get_sys_info
8+
59
import pandas as pd
610

711

@@ -26,21 +30,65 @@
2630
"ignore:Distutils:UserWarning"
2731
)
2832
@pytest.mark.filterwarnings("ignore:Setuptools is replacing distutils:UserWarning")
29-
def test_show_versions(capsys):
33+
@pytest.mark.parametrize("as_json", [True, False, "test_output.json"])
34+
def test_show_versions(capsys, as_json, tmpdir):
3035
# gh-32041
31-
pd.show_versions()
36+
if isinstance(as_json, str):
37+
as_json = os.path.join(tmpdir, as_json)
38+
39+
pd.show_versions(as_json=as_json)
3240
captured = capsys.readouterr()
3341
result = captured.out
3442

35-
# check header
36-
assert "INSTALLED VERSIONS" in result
43+
# check header for non-JSON console output
44+
if as_json is False:
45+
assert "INSTALLED VERSIONS" in result
46+
47+
# check full commit hash
48+
assert re.search(r"commit\s*:\s[0-9a-f]{40}\n", result)
49+
50+
# check required dependency
51+
# 2020-12-09 npdev has "dirty" in the tag
52+
assert re.search(r"numpy\s*:\s([0-9\.\+a-g\_]|dev)+(dirty)?\n", result)
53+
54+
# check optional dependency
55+
assert re.search(r"pyarrow\s*:\s([0-9\.]+|None)\n", result)
56+
57+
# Dictionary-based asserts
58+
else:
59+
# check valid json is printed to the console if as_json is True
60+
if as_json is True:
61+
dict_check = json.loads(result)
62+
elif isinstance(as_json, str):
63+
# make sure that the file was created
64+
assert os.path.exists(as_json)
65+
66+
with open(as_json) as fd:
67+
contents = fd.readlines()
68+
str_contents = "".join(contents)
69+
70+
# make sure that there was output to the file
71+
assert str_contents
72+
73+
# check if file output is valid JSON
74+
dict_check = json.loads(str_contents)
75+
76+
# Basic check that each version element is found in output
77+
version_elements = {
78+
"system": _get_sys_info(),
79+
"dependencies": _get_dependency_info(),
80+
}
81+
82+
assert version_elements == dict_check
83+
3784

38-
# check full commit hash
39-
assert re.search(r"commit\s*:\s[0-9a-f]{40}\n", result)
85+
def test_json_output_match(capsys, tmpdir):
86+
pd.show_versions(as_json=True)
87+
result_console = capsys.readouterr().out
4088

41-
# check required dependency
42-
# 2020-12-09 npdev has "dirty" in the tag
43-
assert re.search(r"numpy\s*:\s([0-9\.\+a-g\_]|dev)+(dirty)?\n", result)
89+
out_path = os.path.join(tmpdir, "test_json.json")
90+
pd.show_versions(as_json=out_path)
91+
with open(out_path) as out_fd:
92+
result_file = "".join(out_fd.readlines())
4493

45-
# check optional dependency
46-
assert re.search(r"pyarrow\s*:\s([0-9\.]+|None)\n", result)
94+
assert result_console == result_file

pandas/util/_print_versions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def show_versions(as_json: Union[str, bool] = False) -> None:
107107
j = {"system": sys_info, "dependencies": deps}
108108

109109
if as_json is True:
110-
print(j)
110+
sys.stdout.writelines(json.dumps(j, indent=2))
111111
else:
112112
assert isinstance(as_json, str) # needed for mypy
113113
with codecs.open(as_json, "wb", encoding="utf8") as f:

0 commit comments

Comments
 (0)