1"""Module containing bug report helper(s)."""
2from __future__ import print_function
3
4import json
5import platform
6import sys
7import ssl
8
9import idna
10import urllib3
11import chardet
12
13from . import __version__ as requests_version
14
15try:
16    from urllib3.contrib import pyopenssl
17except ImportError:
18    pyopenssl = None
19    OpenSSL = None
20    cryptography = None
21else:
22    import OpenSSL
23    import cryptography
24
25
26def _implementation():
27    """Return a dict with the Python implementation and version.
28
29    Provide both the name and the version of the Python implementation
30    currently running. For example, on CPython 2.7.5 it will return
31    {'name': 'CPython', 'version': '2.7.5'}.
32
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
39    if implementation == 'CPython':
40        implementation_version = platform.python_version()
41    elif implementation == 'PyPy':
42        implementation_version = '%s.%s.%s' % (sys.pypy_version_info.major,
43                                               sys.pypy_version_info.minor,
44                                               sys.pypy_version_info.micro)
45        if sys.pypy_version_info.releaselevel != 'final':
46            implementation_version = ''.join([
47                implementation_version, sys.pypy_version_info.releaselevel
48            ])
49    elif implementation == 'Jython':
50        implementation_version = platform.python_version()  # Complete Guess
51    elif implementation == 'IronPython':
52        implementation_version = platform.python_version()  # Complete Guess
53    else:
54        implementation_version = 'Unknown'
55
56    return {'name': implementation, 'version': implementation_version}
57
58
59def info():
60    """Generate information for a bug report."""
61    try:
62        platform_info = {
63            'system': platform.system(),
64            'release': platform.release(),
65        }
66    except IOError:
67        platform_info = {
68            'system': 'Unknown',
69            'release': 'Unknown',
70        }
71
72    implementation_info = _implementation()
73    urllib3_info = {'version': urllib3.__version__}
74    chardet_info = {'version': chardet.__version__}
75
76    pyopenssl_info = {
77        'version': None,
78        'openssl_version': '',
79    }
80    if OpenSSL:
81        pyopenssl_info = {
82            'version': OpenSSL.__version__,
83            'openssl_version': '%x' % OpenSSL.SSL.OPENSSL_VERSION_NUMBER,
84        }
85    cryptography_info = {
86        'version': getattr(cryptography, '__version__', ''),
87    }
88    idna_info = {
89        'version': getattr(idna, '__version__', ''),
90    }
91
92    system_ssl = ssl.OPENSSL_VERSION_NUMBER
93    system_ssl_info = {
94        'version': '%x' % system_ssl if system_ssl is not None else ''
95    }
96
97    return {
98        'platform': platform_info,
99        'implementation': implementation_info,
100        'system_ssl': system_ssl_info,
101        'using_pyopenssl': pyopenssl is not None,
102        'pyOpenSSL': pyopenssl_info,
103        'urllib3': urllib3_info,
104        'chardet': chardet_info,
105        'cryptography': cryptography_info,
106        'idna': idna_info,
107        'requests': {
108            'version': requests_version,
109        },
110    }
111
112
113def main():
114    """Pretty-print the bug information as JSON."""
115    print(json.dumps(info(), sort_keys=True, indent=2))
116
117
118if __name__ == '__main__':
119    main()
120