← 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 |