1from __future__ import annotations
2
3import codecs
4import json
5import locale
6import os
7import platform
8import struct
9import sys
10
11from pandas._typing import JSONSerializable
12from pandas.compat._optional import (
13 VERSIONS,
14 get_version,
15 import_optional_dependency,
16)
17
18
19def _get_commit_hash() -> str | None:
20 """
21 Use vendored versioneer code to get git hash, which handles
22 git worktree correctly.
23 """
24 from pandas._version import get_versions
25
26 versions = get_versions()
27 return versions["full-revisionid"]
28
29
30def _get_sys_info() -> dict[str, JSONSerializable]:
31 """
32 Returns system information as a JSON serializable dictionary.
33 """
34 uname_result = platform.uname()
35 language_code, encoding = locale.getlocale()
36 return {
37 "commit": _get_commit_hash(),
38 "python": ".".join([str(i) for i in sys.version_info]),
39 "python-bits": struct.calcsize("P") * 8,
40 "OS": uname_result.system,
41 "OS-release": uname_result.release,
42 "Version": uname_result.version,
43 "machine": uname_result.machine,
44 "processor": uname_result.processor,
45 "byteorder": sys.byteorder,
46 "LC_ALL": os.environ.get("LC_ALL"),
47 "LANG": os.environ.get("LANG"),
48 "LOCALE": {"language-code": language_code, "encoding": encoding},
49 }
50
51
52def _get_dependency_info() -> dict[str, JSONSerializable]:
53 """
54 Returns dependency information as a JSON serializable dictionary.
55 """
56 deps = [
57 "pandas",
58 # required
59 "numpy",
60 "pytz",
61 "dateutil",
62 # install / build,
63 "setuptools",
64 "pip",
65 "Cython",
66 # test
67 "pytest",
68 "hypothesis",
69 # docs
70 "sphinx",
71 # Other, need a min version
72 "blosc",
73 "feather",
74 "xlsxwriter",
75 "lxml.etree",
76 "html5lib",
77 "pymysql",
78 "psycopg2",
79 "jinja2",
80 # Other, not imported.
81 "IPython",
82 "pandas_datareader",
83 ]
84 deps.extend(list(VERSIONS))
85
86 result: dict[str, JSONSerializable] = {}
87 for modname in deps:
88 mod = import_optional_dependency(modname, errors="ignore")
89 result[modname] = get_version(mod) if mod else None
90 return result
91
92
93def show_versions(as_json: str | bool = False) -> None:
94 """
95 Provide useful information, important for bug reports.
96
97 It comprises info about hosting operation system, pandas version,
98 and versions of other installed relative packages.
99
100 Parameters
101 ----------
102 as_json : str or bool, default False
103 * If False, outputs info in a human readable form to the console.
104 * If str, it will be considered as a path to a file.
105 Info will be written to that file in JSON format.
106 * If True, outputs info in JSON format to the console.
107 """
108 sys_info = _get_sys_info()
109 deps = _get_dependency_info()
110
111 if as_json:
112 j = {"system": sys_info, "dependencies": deps}
113
114 if as_json is True:
115 sys.stdout.writelines(json.dumps(j, indent=2))
116 else:
117 assert isinstance(as_json, str) # needed for mypy
118 with codecs.open(as_json, "wb", encoding="utf8") as f:
119 json.dump(j, f, indent=2)
120
121 else:
122 assert isinstance(sys_info["LOCALE"], dict) # needed for mypy
123 language_code = sys_info["LOCALE"]["language-code"]
124 encoding = sys_info["LOCALE"]["encoding"]
125 sys_info["LOCALE"] = f"{language_code}.{encoding}"
126
127 maxlen = max(len(x) for x in deps)
128 print("\nINSTALLED VERSIONS")
129 print("------------------")
130 for k, v in sys_info.items():
131 print(f"{k:<{maxlen}}: {v}")
132 print("")
133 for k, v in deps.items():
134 print(f"{k:<{maxlen}}: {v}")