user_agent.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. # -*- coding: utf-8 -*-
  2. import collections
  3. import platform
  4. import sys
  5. def user_agent(name, version, extras=None):
  6. """Return an internet-friendly user_agent string.
  7. The majority of this code has been wilfully stolen from the equivalent
  8. function in Requests.
  9. :param name: The intended name of the user-agent, e.g. "python-requests".
  10. :param version: The version of the user-agent, e.g. "0.0.1".
  11. :param extras: List of two-item tuples that are added to the user-agent
  12. string.
  13. :returns: Formatted user-agent string
  14. :rtype: str
  15. """
  16. if extras is None:
  17. extras = []
  18. return UserAgentBuilder(
  19. name, version
  20. ).include_extras(
  21. extras
  22. ).include_implementation(
  23. ).include_system().build()
  24. class UserAgentBuilder(object):
  25. """Class to provide a greater level of control than :func:`user_agent`.
  26. This is used by :func:`user_agent` to build its User-Agent string.
  27. .. code-block:: python
  28. user_agent_str = UserAgentBuilder(
  29. name='requests-toolbelt',
  30. version='17.4.0',
  31. ).include_implementation(
  32. ).include_system(
  33. ).include_extras([
  34. ('requests', '2.14.2'),
  35. ('urllib3', '1.21.2'),
  36. ]).build()
  37. """
  38. format_string = '%s/%s'
  39. def __init__(self, name, version):
  40. """Initialize our builder with the name and version of our user agent.
  41. :param str name:
  42. Name of our user-agent.
  43. :param str version:
  44. The version string for user-agent.
  45. """
  46. self._pieces = collections.deque([(name, version)])
  47. def build(self):
  48. """Finalize the User-Agent string.
  49. :returns:
  50. Formatted User-Agent string.
  51. :rtype:
  52. str
  53. """
  54. return " ".join([self.format_string % piece for piece in self._pieces])
  55. def include_extras(self, extras):
  56. """Include extra portions of the User-Agent.
  57. :param list extras:
  58. list of tuples of extra-name and extra-version
  59. """
  60. if any(len(extra) != 2 for extra in extras):
  61. raise ValueError('Extras should be a sequence of two item tuples.')
  62. self._pieces.extend(extras)
  63. return self
  64. def include_implementation(self):
  65. """Append the implementation string to the user-agent string.
  66. This adds the the information that you're using CPython 2.7.13 to the
  67. User-Agent.
  68. """
  69. self._pieces.append(_implementation_tuple())
  70. return self
  71. def include_system(self):
  72. """Append the information about the Operating System."""
  73. self._pieces.append(_platform_tuple())
  74. return self
  75. def _implementation_tuple():
  76. """Return the tuple of interpreter name and version.
  77. Returns a string that provides both the name and the version of the Python
  78. implementation currently running. For example, on CPython 2.7.5 it will
  79. return "CPython/2.7.5".
  80. This function works best on CPython and PyPy: in particular, it probably
  81. doesn't work for Jython or IronPython. Future investigation should be done
  82. to work out the correct shape of the code for those platforms.
  83. """
  84. implementation = platform.python_implementation()
  85. if implementation == 'CPython':
  86. implementation_version = platform.python_version()
  87. elif implementation == 'PyPy':
  88. implementation_version = '%s.%s.%s' % (sys.pypy_version_info.major,
  89. sys.pypy_version_info.minor,
  90. sys.pypy_version_info.micro)
  91. if sys.pypy_version_info.releaselevel != 'final':
  92. implementation_version = ''.join([
  93. implementation_version, sys.pypy_version_info.releaselevel
  94. ])
  95. elif implementation == 'Jython':
  96. implementation_version = platform.python_version() # Complete Guess
  97. elif implementation == 'IronPython':
  98. implementation_version = platform.python_version() # Complete Guess
  99. else:
  100. implementation_version = 'Unknown'
  101. return (implementation, implementation_version)
  102. def _implementation_string():
  103. return "%s/%s" % _implementation_tuple()
  104. def _platform_tuple():
  105. try:
  106. p_system = platform.system()
  107. p_release = platform.release()
  108. except IOError:
  109. p_system = 'Unknown'
  110. p_release = 'Unknown'
  111. return (p_system, p_release)