1from __future__ import annotations
2
3import codecs
4import json
5import locale
6import os
7import platform
8import struct
9import sys
10from typing import TYPE_CHECKING
11
12if TYPE_CHECKING:
13 from pandas._typing import JSONSerializable
14
15from pandas.compat._optional import (
16 VERSIONS,
17 get_version,
18 import_optional_dependency,
19)
20
21
22def _get_commit_hash() -> str | None:
23 """
24 Use vendored versioneer code to get git hash, which handles
25 git worktree correctly.
26 """
27 try:
28 from pandas._version_meson import ( # pyright: ignore [reportMissingImports]
29 __git_version__,
30 )
31
32 return __git_version__
33 except ImportError:
34 from pandas._version import get_versions
35
36 versions = get_versions()
37 return versions["full-revisionid"]
38
39
40def _get_sys_info() -> dict[str, JSONSerializable]:
41 """
42 Returns system information as a JSON serializable dictionary.
43 """
44 uname_result = platform.uname()
45 language_code, encoding = locale.getlocale()
46 return {
47 "commit": _get_commit_hash(),
48 "python": platform.python_version(),
49 "python-bits": struct.calcsize("P") * 8,
50 "OS": uname_result.system,
51 "OS-release": uname_result.release,
52 "Version": uname_result.version,
53 "machine": uname_result.machine,
54 "processor": uname_result.processor,
55 "byteorder": sys.byteorder,
56 "LC_ALL": os.environ.get("LC_ALL"),
57 "LANG": os.environ.get("LANG"),
58 "LOCALE": {"language-code": language_code, "encoding": encoding},
59 }
60
61
62def _get_dependency_info() -> dict[str, JSONSerializable]:
63 """
64 Returns dependency information as a JSON serializable dictionary.
65 """
66 deps = [
67 "pandas",
68 # required
69 "numpy",
70 "pytz",
71 "dateutil",
72 # install / build,
73 "pip",
74 "Cython",
75 # docs
76 "sphinx",
77 # Other, not imported.
78 "IPython",
79 ]
80 # Optional dependencies
81 deps.extend(list(VERSIONS))
82
83 result: dict[str, JSONSerializable] = {}
84 for modname in deps:
85 try:
86 mod = import_optional_dependency(modname, errors="ignore")
87 except Exception:
88 # Dependency conflicts may cause a non ImportError
89 result[modname] = "N/A"
90 else:
91 result[modname] = get_version(mod) if mod else None
92 return result
93
94
95def show_versions(as_json: str | bool = False) -> None:
96 """
97 Provide useful information, important for bug reports.
98
99 It comprises info about hosting operation system, pandas version,
100 and versions of other installed relative packages.
101
102 Parameters
103 ----------
104 as_json : str or bool, default False
105 * If False, outputs info in a human readable form to the console.
106 * If str, it will be considered as a path to a file.
107 Info will be written to that file in JSON format.
108 * If True, outputs info in JSON format to the console.
109
110 Examples
111 --------
112 >>> pd.show_versions() # doctest: +SKIP
113 Your output may look something like this:
114 INSTALLED VERSIONS
115 ------------------
116 commit : 37ea63d540fd27274cad6585082c91b1283f963d
117 python : 3.10.6.final.0
118 python-bits : 64
119 OS : Linux
120 OS-release : 5.10.102.1-microsoft-standard-WSL2
121 Version : #1 SMP Wed Mar 2 00:30:59 UTC 2022
122 machine : x86_64
123 processor : x86_64
124 byteorder : little
125 LC_ALL : None
126 LANG : en_GB.UTF-8
127 LOCALE : en_GB.UTF-8
128 pandas : 2.0.1
129 numpy : 1.24.3
130 ...
131 """
132 sys_info = _get_sys_info()
133 deps = _get_dependency_info()
134
135 if as_json:
136 j = {"system": sys_info, "dependencies": deps}
137
138 if as_json is True:
139 sys.stdout.writelines(json.dumps(j, indent=2))
140 else:
141 assert isinstance(as_json, str) # needed for mypy
142 with codecs.open(as_json, "wb", encoding="utf8") as f:
143 json.dump(j, f, indent=2)
144
145 else:
146 assert isinstance(sys_info["LOCALE"], dict) # needed for mypy
147 language_code = sys_info["LOCALE"]["language-code"]
148 encoding = sys_info["LOCALE"]["encoding"]
149 sys_info["LOCALE"] = f"{language_code}.{encoding}"
150
151 maxlen = max(len(x) for x in deps)
152 print("\nINSTALLED VERSIONS")
153 print("------------------")
154 for k, v in sys_info.items():
155 print(f"{k:<{maxlen}}: {v}")
156 print("")
157 for k, v in deps.items():
158 print(f"{k:<{maxlen}}: {v}")