help.py 4.1 KB

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