← requests  /  src/requests/help.py

1
"""Module containing bug report helper(s)."""
2
3
# pyright: reportUnknownMemberType=false
4
5
import json
6
import platform
7
import ssl
8
import sys
9
from typing import Any
10
11
import idna
12
import urllib3
13
14
from . import __version__ as requests_version
15
16
try:
17
    import charset_normalizer
18
except ImportError:
19
    charset_normalizer = None
20
21
try:
22
    import chardet  # type: ignore[import-not-found]
23
except ImportError:
24
    chardet = None
25
26
try:
27
    from urllib3.contrib import pyopenssl
28
except ImportError:
29
    pyopenssl = None
30
    OpenSSL = None
31
    cryptography = None
32
else:
33
    import cryptography  # type: ignore[import-not-found]
34
    import OpenSSL  # type: ignore[import-not-found]
35
36
37
def _implementation():
38
    """Return a dict with the Python implementation and version.
39
40
    Provide both the name and the version of the Python implementation
41
    currently running. For example, on CPython 3.10.3 it will return
42
    {'name': 'CPython', 'version': '3.10.3'}.
43
44
    This function works best on CPython and PyPy: in particular, it probably
45
    doesn't work for Jython or IronPython. Future investigation should be done
46
    to work out the correct shape of the code for those platforms.
47
    """
48
    implementation = platform.python_implementation()
49
50
    if implementation == "CPython":
51
        implementation_version = platform.python_version()
52
    elif implementation == "PyPy":
53
        pypy = sys.pypy_version_info  # type: ignore[attr-defined]
54
        implementation_version = f"{pypy.major}.{pypy.minor}.{pypy.micro}"
55
        if sys.pypy_version_info.releaselevel != "final":  # type: ignore[attr-defined]
56
            implementation_version = "".join(
57
                [implementation_version, sys.pypy_version_info.releaselevel]  # type: ignore[attr-defined]
58
            )
59
    elif implementation == "Jython":
60
        implementation_version = platform.python_version()  # Complete Guess
61
    elif implementation == "IronPython":
62
        implementation_version = platform.python_version()  # Complete Guess
63
    else:
64
        implementation_version = "Unknown"
65
66
    return {"name": implementation, "version": implementation_version}
67
68
69
def info() -> dict[str, Any]:
70
    """Generate information for a bug report."""
71
    try:
72
        platform_info = {
73
            "system": platform.system(),
74
            "release": platform.release(),
75
        }
76
    except OSError:
77
        platform_info = {
78
            "system": "Unknown",
79
            "release": "Unknown",
80
        }
81
82
    implementation_info = _implementation()
83
    urllib3_info = {"version": urllib3.__version__}  # type: ignore[reportPrivateImportUsage]
84
    charset_normalizer_info = {"version": None}
85
    chardet_info: dict[str, str | None] = {"version": None}
86
    if charset_normalizer:
87
        charset_normalizer_info = {"version": charset_normalizer.__version__}
88
    if chardet:
89
        chardet_info = {"version": chardet.__version__}
90
91
    pyopenssl_info: dict[str, str | None] = {
92
        "version": None,
93
        "openssl_version": "",
94
    }
95
    if OpenSSL:
96
        pyopenssl_info = {
97
            "version": OpenSSL.__version__,
98
            "openssl_version": f"{OpenSSL.SSL.OPENSSL_VERSION_NUMBER:x}",
99
        }
100
    cryptography_info = {
101
        "version": getattr(cryptography, "__version__", ""),
102
    }
103
    idna_info = {
104
        "version": getattr(idna, "__version__", ""),
105
    }
106
107
    system_ssl = ssl.OPENSSL_VERSION_NUMBER
108
    system_ssl_info = {"version": f"{system_ssl:x}" if system_ssl is not None else ""}  # type: ignore[reportUnnecessaryComparison]
109
110
    return {
111
        "platform": platform_info,
112
        "implementation": implementation_info,
113
        "system_ssl": system_ssl_info,
114
        "using_pyopenssl": pyopenssl is not None,
115
        "using_charset_normalizer": chardet is None,
116
        "pyOpenSSL": pyopenssl_info,
117
        "urllib3": urllib3_info,
118
        "chardet": chardet_info,
119
        "charset_normalizer": charset_normalizer_info,
120
        "cryptography": cryptography_info,
121
        "idna": idna_info,
122
        "requests": {
123
            "version": requests_version,
124
        },
125
    }
126
127
128
def main():
129
    """Pretty-print the bug information as JSON."""
130
    print(json.dumps(info(), sort_keys=True, indent=2))
131
132
133
if __name__ == "__main__":
134
    main()
135